diff --git a/libs/License-R.class.txt b/libs/License-R.class.txt new file mode 100644 index 0000000..4a01aac --- /dev/null +++ b/libs/License-R.class.txt @@ -0,0 +1,316 @@ + +RedBeanPHP +Written by Gabor de Mooij + +RedBean is DUAL Licensed New BSD and GPLv2. You may choose the license that fits +best for your project. + +New BSD License + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: +* Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. +* Redistributions in binary form must reproduce the above copyright +notice, this list of conditions and the following disclaimer in the +documentation and/or other materials provided with the distribution. +* Neither the name of RedBeanPHP nor the +names of its contributors may be used to endorse or promote products +derived from this software without specific prior written permission. + + +THIS SOFTWARE IS PROVIDED BY GABOR DE MOOIJ ''AS IS'' AND ANY +EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL GABOR DE MOOIJ BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +RedBeanPHP is Written by Gabor de Mooij (G.J.G.T de Mooij) Copyright (c) 2014. + + +GPLv2 LICENSE + + + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. diff --git a/libs/R.class.php b/libs/R.class.php index 862dcbc..a851d8d 100644 --- a/libs/R.class.php +++ b/libs/R.class.php @@ -1,21 +1,344 @@ mode === 0 ) { + echo $log; + } else { + $this->logs[] = $log; + } + } else { + if ( $this->mode === 0 ) { + echo $argument; + } else { + $this->logs[] = $argument; + } + } + + if ($this->mode === 0) echo "
\n"; + } + } + + /** + * Returns the logs array. + * + * @return array + */ + public function getLogs() + { + return $this->logs; + } + + /** + * Empties the logs array. + * + * @return self + */ + public function clear() + { + $this->logs = array(); + return $this; + } + + /** + * Selects a logging mode. + * Mode 0 means echoing all statements, while mode 1 + * means populating the logs array. + * + * @param integer $mode mode + * + * @return self + */ + public function setMode( $mode ) + { + if ($mode !== 0 && $mode !== 1) { + throw new RedException( 'Invalid mode selected for logger, use 1 or 0.' ); + } + $this->mode = $mode; + return $this; + } + + /** + * Searches for all log entries in internal log array + * for $needle and returns those entries. + * + * @param string $needle needle + * + * @return array + */ + public function grep( $needle ) + { + $found = array(); + foreach( $this->logs as $logEntry ) { + if (strpos( $logEntry, $needle ) !== false) $found[] = $logEntry; + } + return $found; + } +} +} + +namespace RedBeanPHP\Logger\RDefault { + +use RedBeanPHP\Logger as Logger; +use RedBeanPHP\Logger\RDefault as RDefault; +use RedBeanPHP\RedException as RedException; +use RedBeanPHP\RedException\Security as Security; + +/** + * Debug logger. + * A special logger for debugging purposes. + * + * @file RedBean/Logger/RDefault/Debug.php + * @desc Debug Logger + * @author Gabor de Mooij and the RedBeanPHP Community + * @license BSD/GPLv2 + * + * Provides a debugging logging functions for RedBeanPHP. + * + * copyright (c) G.J.G.T. (Gabor) de Mooij + * This source file is subject to the BSD/GPLv2 License that is bundled + * with this source code in the file license.txt. + */ +class Debug extends RDefault implements Logger +{ + + /** + * Writes a query for logging with all bindings / params filled + * in. + * + * @param string $newSql the query + * @param array $bindings the bindings to process (key-value pairs) + * + * @return string + */ + private function writeQuery( $newSql, $newBindings ) + { + $newStr = $newSql; + foreach( $newBindings as $slot => $value ) { + if ( strpos( $slot, ':' ) === 0 ) { + $newStr = str_replace( $slot, $this->fillInValue( $value ), $newStr ); + } + } + return $newStr; + } + + /** + * Fills in a value of a binding and truncates the + * resulting string if necessary. + * + * @param mixed $value + * + * @return string + */ + protected function fillInValue( $value ) + { + if ( is_null( $value ) ) $value = 'NULL'; + + $value = strval( $value ); + if ( strlen( $value ) > 20 ) { + $value = substr( $value, 0, 20 ).'... '; + } + + if ( !is_numeric( $value ) && $value !== 'NULL') { + $value = '\''.$value.'\''; + } + + return $value; + } + + /** + * Dependending on the current mode of operation, + * this method will either log and output to STDIN or + * just log. + * + * @param string $str string to log or output and log + * + * @return void + */ + protected function output( $str ) + { + $this->logs[] = $str; + if ( !$this->mode ) echo $str .'
'; + } + + /** + * Normalizes the slots in an SQL string. + * Replaces question mark slots with :slot1 :slot2 etc. + * + * @param string $sql sql to normalize + * + * @return string + */ + protected function normalizeSlots( $sql ) + { + $i = 0; + $newSql = $sql; + while($i < 20 && strpos($newSql, '?') !== FALSE ){ + $pos = strpos( $newSql, '?' ); + $slot = ':slot'.$i; + $begin = substr( $newSql, 0, $pos ); + $end = substr( $newSql, $pos+1 ); + $newSql = $begin . $slot . $end; + $i++; + } + return $newSql; + } + + /** + * Normalizes the bindings. + * Replaces numeric binding keys with :slot1 :slot2 etc. + * + * @param array $bindings bindings to normalize + * + * @return array + */ + protected function normalizeBindings( $bindings ) + { + $i = 0; + $newBindings = array(); + foreach( $bindings as $key => $value ) { + if ( is_numeric($key) ) { + $newKey = ':slot'.$i; + $newBindings[$newKey] = $value; + $i++; + } else { + $newBindings[$key] = $value; + } + } + return $newBindings; + } + + /** + * Logger method. + * + * Takes a number of arguments tries to create + * a proper debug log based on the available data. + * + * @return void + */ + public function log() + { + if ( func_num_args() < 1 ) return; + + $sql = func_get_arg( 0 ); + + if ( func_num_args() < 2) { + $bindings = array(); + } else { + $bindings = func_get_arg( 1 ); + } + + if ( !is_array( $bindings ) ) { + return $this->output( $sql ); + } + + $newSql = $this->normalizeSlots( $sql ); + $newBindings = $this->normalizeBindings( $bindings ); + $newStr = $this->writeQuery( $newSql, $newBindings ); + $this->output( $newStr ); + } +} +} + +namespace RedBeanPHP { + +/** + * Interface for database drivers + * + * @file RedBean/Driver.php + * @desc Describes the API for database classes + * @author Gabor de Mooij and the RedBeanPHP Community + * @license BSD/GPLv2 + * + * The Driver API conforms to the ADODB pseudo standard + * for database drivers. + * + * copyright (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community + * This source file is subject to the BSD/GPLv2 License that is bundled + * with this source code in the file license.txt. + */ +interface Driver { /** @@ -137,10 +460,37 @@ interface RedBean_Driver */ public function FailTrans(); } +} +namespace RedBeanPHP\Driver { -class RedBean_Driver_PDO implements RedBean_Driver +use RedBeanPHP\Driver as Driver; +use RedBeanPHP\Logger as Logger; +use RedBeanPHP\QueryWriter\AQueryWriter as AQueryWriter; +use RedBeanPHP\RedException\SQL as SQL; +use RedBeanPHP\Logger\RDefault as RDefault; +use RedBeanPHP\PDOCompatible as PDOCompatible; + +/** + *\PDO Driver + * This Driver implements the RedBean Driver API + * + * @file RedBean/PDO.php + * @desc \PDO Driver + * @author Gabor de Mooij and the RedBeanPHP Community, Desfrenes + * @license BSD/GPLv2 + * + * (c) copyright Desfrenes & Gabor de Mooij and the RedBeanPHP community + * This source file is subject to the BSD/GPLv2 License that is bundled + * with this source code in the file license.txt. + */ +class RPDO implements Driver { + /** + * @var integer + */ + protected $max; + /** * @var string */ @@ -152,12 +502,12 @@ class RedBean_Driver_PDO implements RedBean_Driver protected $debug = FALSE; /** - * @var RedBean_Logger + * @var Logger */ protected $logger = NULL; /** - * @var PDO + * @var\PDO */ protected $pdo; @@ -192,16 +542,11 @@ class RedBean_Driver_PDO implements RedBean_Driver protected $mysqlEncoding = ''; /** - * @var boolean - */ - protected $autoSetEncoding = TRUE; - - /** - * Binds parameters. This method binds parameters to a PDOStatement for + * Binds parameters. This method binds parameters to a\PDOStatement for * Query Execution. This method binds parameters as NULL, INTEGER or STRING * and supports both named keys and question mark keys. * - * @param PDOStatement $statement PDO Statement instance + * @param \PDOStatement $statement \PDO Statement instance * @param array $bindings values that need to get bound to the statement * * @return void @@ -211,19 +556,19 @@ class RedBean_Driver_PDO implements RedBean_Driver foreach ( $bindings as $key => &$value ) { if ( is_integer( $key ) ) { if ( is_null( $value ) ) { - $statement->bindValue( $key + 1, NULL, PDO::PARAM_NULL ); - } elseif ( !$this->flagUseStringOnlyBinding && RedBean_QueryWriter_AQueryWriter::canBeTreatedAsInt( $value ) && $value < 2147483648 ) { - $statement->bindParam( $key + 1, $value, PDO::PARAM_INT ); + $statement->bindValue( $key + 1, NULL,\PDO::PARAM_NULL ); + } elseif ( !$this->flagUseStringOnlyBinding && AQueryWriter::canBeTreatedAsInt( $value ) && $value <= $this->max ) { + $statement->bindParam( $key + 1, $value,\PDO::PARAM_INT ); } else { - $statement->bindParam( $key + 1, $value, PDO::PARAM_STR ); + $statement->bindParam( $key + 1, $value,\PDO::PARAM_STR ); } } else { if ( is_null( $value ) ) { - $statement->bindValue( $key, NULL, PDO::PARAM_NULL ); - } elseif ( !$this->flagUseStringOnlyBinding && RedBean_QueryWriter_AQueryWriter::canBeTreatedAsInt( $value ) && $value < 2147483648 ) { - $statement->bindParam( $key, $value, PDO::PARAM_INT ); + $statement->bindValue( $key, NULL,\PDO::PARAM_NULL ); + } elseif ( !$this->flagUseStringOnlyBinding && AQueryWriter::canBeTreatedAsInt( $value ) && $value <= $this->max ) { + $statement->bindParam( $key, $value,\PDO::PARAM_INT ); } else { - $statement->bindParam( $key, $value, PDO::PARAM_STR ); + $statement->bindParam( $key, $value,\PDO::PARAM_STR ); } } } @@ -241,7 +586,7 @@ class RedBean_Driver_PDO implements RedBean_Driver * * @return void * - * @throws RedBean_Exception_SQL + * @throws SQL */ protected function runQuery( $sql, $bindings, $options = array() ) { @@ -253,7 +598,7 @@ class RedBean_Driver_PDO implements RedBean_Driver try { if ( strpos( 'pgsql', $this->dsn ) === 0 ) { - $statement = $this->pdo->prepare( $sql, array( PDO::PGSQL_ATTR_DISABLE_NATIVE_PREPARED_STATEMENT => TRUE ) ); + $statement = $this->pdo->prepare( $sql, array(\PDO::PGSQL_ATTR_DISABLE_NATIVE_PREPARED_STATEMENT => TRUE ) ); } else { $statement = $this->pdo->prepare( $sql ); } @@ -265,9 +610,9 @@ class RedBean_Driver_PDO implements RedBean_Driver $this->affectedRows = $statement->rowCount(); if ( $statement->columnCount() ) { - + $fetchStyle = ( isset( $options['fetchStyle'] ) ) ? $options['fetchStyle'] : NULL; - + $this->resultArray = $statement->fetchAll( $fetchStyle ); if ( $this->debug && $this->logger ) { @@ -276,14 +621,14 @@ class RedBean_Driver_PDO implements RedBean_Driver } else { $this->resultArray = array(); } - } catch ( PDOException $e ) { + } catch (\PDOException $e ) { //Unfortunately the code field is supposed to be int by default (php) //So we need a property to convey the SQL State code. $err = $e->getMessage(); if ( $this->debug && $this->logger ) $this->logger->log( 'An error occurred: ' . $err ); - $exception = new RedBean_Exception_SQL( $err, 0 ); + $exception = new SQL( $err, 0 ); $exception->setSQLState( $e->getCode() ); throw $exception; @@ -291,29 +636,29 @@ class RedBean_Driver_PDO implements RedBean_Driver } /** - * Try to fix MySQL character encoding problems. - * MySQL < 5.5 does not support proper 4 byte unicode but they - * seem to have added it with version 5.5 under a different label: utf8mb4. - * We try to select the best possible charset based on your version data. - */ + * Try to fix MySQL character encoding problems. + * MySQL < 5.5 does not support proper 4 byte unicode but they + * seem to have added it with version 5.5 under a different label: utf8mb4. + * We try to select the best possible charset based on your version data. + */ protected function setEncoding() { - $driver = $this->pdo->getAttribute( PDO::ATTR_DRIVER_NAME ); - $version = floatval( $this->pdo->getAttribute( PDO::ATTR_SERVER_VERSION ) ); + $driver = $this->pdo->getAttribute(\PDO::ATTR_DRIVER_NAME ); + $version = floatval( $this->pdo->getAttribute(\PDO::ATTR_SERVER_VERSION ) ); - if ( $driver === 'mysql' ) { + if ($driver === 'mysql') { $encoding = ($version >= 5.5) ? 'utf8mb4' : 'utf8'; - $this->pdo->setAttribute( PDO::MYSQL_ATTR_INIT_COMMAND, 'SET NAMES '.$encoding ); //on every re-connect + $this->pdo->setAttribute(\PDO::MYSQL_ATTR_INIT_COMMAND, 'SET NAMES '.$encoding ); //on every re-connect $this->pdo->exec(' SET NAMES '. $encoding); //also for current connection $this->mysqlEncoding = $encoding; } } /** - * Returns the best possible encoding for MySQL based on version data. - * - * @return string - */ + * Returns the best possible encoding for MySQL based on version data. + * + * @return string + */ public function getMysqlEncoding() { return $this->mysqlEncoding; @@ -321,31 +666,26 @@ class RedBean_Driver_PDO implements RedBean_Driver /** * Constructor. You may either specify dsn, user and password or - * just give an existing PDO connection. + * just give an existing\PDO connection. * Examples: - * $driver = new RedBean_Driver_PDO($dsn, $user, $password); - * $driver = new RedBean_Driver_PDO($existingConnection); + * $driver = new RPDO($dsn, $user, $password); + * $driver = new RPDO($existingConnection); * - * @param string|PDO $dsn database connection string - * @param string $user optional, usename to sign in - * @param string $pass optional, password for connection login + * @param string|object $dsn database connection string + * @param string $user optional, usename to sign in + * @param string $pass optional, password for connection login * */ - public function __construct( $dsn, $user = NULL, $pass = NULL, $autoSetEncoding = TRUE ) + public function __construct( $dsn, $user = NULL, $pass = NULL ) { - $this->autoSetEncoding = $autoSetEncoding; - - if ( $dsn instanceof PDO ) { + if ( is_object( $dsn ) ) { $this->pdo = $dsn; $this->isConnected = TRUE; - if ( $this->autoSetEncoding !== FALSE ) { - $this->setEncoding(); - } - - $this->pdo->setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION ); - $this->pdo->setAttribute( PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC ); + $this->setEncoding(); + $this->pdo->setAttribute(\PDO::ATTR_ERRMODE,\PDO::ERRMODE_EXCEPTION ); + $this->pdo->setAttribute(\PDO::ATTR_DEFAULT_FETCH_MODE,\PDO::FETCH_ASSOC ); // make sure that the dsn at least contains the type $this->dsn = $this->getDatabaseType(); @@ -354,6 +694,13 @@ class RedBean_Driver_PDO implements RedBean_Driver $this->connectInfo = array( 'pass' => $pass, 'user' => $user ); } + + //PHP 5.3 PDO SQLite has a bug with large numbers: + if ( strpos( $this->dsn, 'sqlite' ) === 0 && PHP_MAJOR_VERSION === 5 && PHP_MINOR_VERSION === 3) { + $this->max = 2147483647; //otherwise you get -2147483648 ?! demonstrated in build #603 on Travis. + } else { + $this->max = PHP_INT_MAX; //the normal value of course (makes it possible to use large numbers in LIMIT clause) + } } /** @@ -369,50 +716,62 @@ class RedBean_Driver_PDO implements RedBean_Driver } /** - * Establishes a connection to the database using PHP PDO - * functionality. If a connection has already been established this - * method will simply return directly. This method also turns on - * UTF8 for the database and PDO-ERRMODE-EXCEPTION as well as - * PDO-FETCH-ASSOC. - * - * @throws PDOException - * - * @return void - */ + * Establishes a connection to the database using PHP\PDO + * functionality. If a connection has already been established this + * method will simply return directly. This method also turns on + * UTF8 for the database and\PDO-ERRMODE-EXCEPTION as well as + *\PDO-FETCH-ASSOC. + * + * @throws\PDOException + * + * @return void + */ public function connect() - { + { if ( $this->isConnected ) return; - try { + try { $user = $this->connectInfo['user']; - $pass = $this->connectInfo['pass']; + $pass = $this->connectInfo['pass']; - $this->pdo = new PDO( + $this->pdo = new\PDO( $this->dsn, - $user, - $pass, - array(PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, - PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, - ) - ); + $user, + $pass, + array(\PDO::ATTR_ERRMODE =>\PDO::ERRMODE_EXCEPTION, + \PDO::ATTR_DEFAULT_FETCH_MODE =>\PDO::FETCH_ASSOC, + ) + ); - if ( $this->autoSetEncoding !== FALSE ) { - $this->setEncoding(); - } - - $this->pdo->setAttribute( PDO::ATTR_STRINGIFY_FETCHES, TRUE ); + $this->setEncoding(); + $this->pdo->setAttribute(\PDO::ATTR_STRINGIFY_FETCHES, TRUE ); $this->isConnected = TRUE; - } catch ( PDOException $exception ) { - $matches = array(); + } catch (\PDOException $exception ) { + $matches = array(); - $dbname = ( preg_match( '/dbname=(\w+)/', $this->dsn, $matches ) ) ? $matches[1] : '?'; + $dbname = ( preg_match( '/dbname=(\w+)/', $this->dsn, $matches ) ) ? $matches[1] : '?'; - throw new PDOException( 'Could not connect to database (' . $dbname . ').', $exception->getCode() ); - } + throw new\PDOException( 'Could not connect to database (' . $dbname . ').', $exception->getCode() ); + } } /** - * @see RedBean_Driver::GetAll + * Directly sets PDO instance into driver. + * This method might improve performance, however since the driver does + * not configure this instance terrible things may happen... only use + * this method if you are an expert on RedBeanPHP, PDO and UTF8 connections and + * you know your database server VERY WELL. + * + * @param PDO $pdo PDO instance + * + * @return void + */ + public function setPDO( \PDO $pdo ) { + $this->pdo = $pdo; + } + + /** + * @see Driver::GetAll */ public function GetAll( $sql, $bindings = array() ) { @@ -420,22 +779,22 @@ class RedBean_Driver_PDO implements RedBean_Driver return $this->resultArray; } - + /** * @see Driver::GetAssocRow */ public function GetAssocRow( $sql, $bindings = array() ) { - $this->runQuery( $sql, $bindings, array( - 'fetchStyle' => PDO::FETCH_ASSOC - ) + $this->runQuery( $sql, $bindings, array( + 'fetchStyle' => \PDO::FETCH_ASSOC + ) ); - + return $this->resultArray; } - + /** - * @see RedBean_Driver::GetCol + * @see Driver::GetCol */ public function GetCol( $sql, $bindings = array() ) { @@ -452,7 +811,7 @@ class RedBean_Driver_PDO implements RedBean_Driver } /** - * @see RedBean_Driver::GetCell + * @see Driver::GetCell */ public function GetCell( $sql, $bindings = array() ) { @@ -465,7 +824,7 @@ class RedBean_Driver_PDO implements RedBean_Driver } /** - * @see RedBean_Driver::GetRow + * @see Driver::GetRow */ public function GetRow( $sql, $bindings = array() ) { @@ -475,7 +834,7 @@ class RedBean_Driver_PDO implements RedBean_Driver } /** - * @see RedBean_Driver::Excecute + * @see Driver::Excecute */ public function Execute( $sql, $bindings = array() ) { @@ -485,7 +844,7 @@ class RedBean_Driver_PDO implements RedBean_Driver } /** - * @see RedBean_Driver::GetInsertID + * @see Driver::GetInsertID */ public function GetInsertID() { @@ -495,7 +854,7 @@ class RedBean_Driver_PDO implements RedBean_Driver } /** - * @see RedBean_Driver::Affected_Rows + * @see Driver::Affected_Rows */ public function Affected_Rows() { @@ -510,7 +869,7 @@ class RedBean_Driver_PDO implements RedBean_Driver * results. * * @param boolean $trueFalse turn on/off - * @param RedBean_Logger $logger logger instance + * @param Logger $logger logger instance * * @return void */ @@ -521,28 +880,28 @@ class RedBean_Driver_PDO implements RedBean_Driver $this->debug = (bool) $tf; if ( $this->debug and !$logger ) { - $logger = new RedBean_Logger_Default(); + $logger = new RDefault(); } $this->setLogger( $logger ); } /** - * Injects RedBean_Logger object. + * Injects Logger object. * Sets the logger instance you wish to use. * - * @param RedBean_Logger $logger the logger instance to be used for logging + * @param Logger $logger the logger instance to be used for logging */ - public function setLogger( RedBean_Logger $logger ) + public function setLogger( Logger $logger ) { $this->logger = $logger; } /** - * Gets RedBean_Logger object. - * Returns the currently active RedBean_Logger instance. + * Gets Logger object. + * Returns the currently active Logger instance. * - * @return RedBean_Logger + * @return Logger */ public function getLogger() { @@ -550,7 +909,7 @@ class RedBean_Driver_PDO implements RedBean_Driver } /** - * @see RedBean_Driver::StartTrans + * @see Driver::StartTrans */ public function StartTrans() { @@ -560,7 +919,7 @@ class RedBean_Driver_PDO implements RedBean_Driver } /** - * @see RedBean_Driver::CommitTrans + * @see Driver::CommitTrans */ public function CommitTrans() { @@ -570,7 +929,7 @@ class RedBean_Driver_PDO implements RedBean_Driver } /** - * @see RedBean_Driver::FailTrans + * @see Driver::FailTrans */ public function FailTrans() { @@ -580,9 +939,9 @@ class RedBean_Driver_PDO implements RedBean_Driver } /** - * Returns the name of database driver for PDO. - * Uses the PDO attribute DRIVER NAME to obtain the name of the - * PDO driver. + * Returns the name of database driver for\PDO. + * Uses the\PDO attribute DRIVER NAME to obtain the name of the + *\PDO driver. * * @return string */ @@ -590,7 +949,7 @@ class RedBean_Driver_PDO implements RedBean_Driver { $this->connect(); - return $this->pdo->getAttribute( PDO::ATTR_DRIVER_NAME ); + return $this->pdo->getAttribute(\PDO::ATTR_DRIVER_NAME ); } /** @@ -602,13 +961,13 @@ class RedBean_Driver_PDO implements RedBean_Driver { $this->connect(); - return $this->pdo->getAttribute( PDO::ATTR_CLIENT_VERSION ); + return $this->pdo->getAttribute(\PDO::ATTR_CLIENT_VERSION ); } /** - * Returns the underlying PHP PDO instance. + * Returns the underlying PHP\PDO instance. * - * @return PDO + * @return\PDO */ public function getPDO() { @@ -618,7 +977,7 @@ class RedBean_Driver_PDO implements RedBean_Driver } /** - * Closes database connection by destructing PDO. + * Closes database connection by destructing\PDO. * * @return void */ @@ -629,7 +988,7 @@ class RedBean_Driver_PDO implements RedBean_Driver } /** - * Returns TRUE if the current PDO instance is connected. + * Returns TRUE if the current\PDO instance is connected. * * @return boolean */ @@ -638,103 +997,128 @@ class RedBean_Driver_PDO implements RedBean_Driver return $this->isConnected && $this->pdo; } } +} +namespace RedBeanPHP { -class RedBean_OODBBean implements IteratorAggregate, ArrayAccess, Countable +use RedBeanPHP\QueryWriter\AQueryWriter as AQueryWriter; +use RedBeanPHP\BeanHelper as BeanHelper; +use RedBeanPHP\RedException\Security as Security; +use RedBeanPHP\RedException as RedException; +use RedBeanPHP\OODBBean as OODBBean; + +/** + * OODBBean (Object Oriented DataBase Bean) + * + * @file RedBean/OODBBean.php + * @desc The Bean class used for passing information + * @author Gabor de Mooij and the RedBeanPHP community + * @license BSD/GPLv2 + * + * copyright (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community. + * This source file is subject to the BSD/GPLv2 License that is bundled + * with this source code in the file license.txt. + */ +class OODBBean implements\IteratorAggregate,\ArrayAccess,\Countable { - - /** - * Setting: use beautiful columns, i.e. turn camelcase column names into snake case column names - * for database. - * - * @var boolean - */ - private static $flagUseBeautyCols = TRUE; - - /** - * Setting: use IDs as keys when exporting. By default this has been turned off because exports - * to Javascript may cause problems due to Javascript Sparse Array implementation (i.e. causing large arrays - * with lots of 'gaps'). - * - * @var boolean - */ - private static $flagKeyedExport = FALSE; - - /** - * Whether to skip beautification of columns or not. - * - * @var boolean - */ - private $flagSkipBeau = FALSE; - /** * This is where the real properties of the bean live. They are stored and retrieved * by the magic getter and setter (__get and __set). * * @var array $properties */ - private $properties = array(); + protected $properties = array(); /** * Here we keep the meta data of a bean. * * @var array */ - private $__info = array(); + protected $__info = array(); /** * The BeanHelper allows the bean to access the toolbox objects to implement * rich functionality, otherwise you would have to do everything with R or * external objects. * - * @var RedBean_BeanHelper + * @var BeanHelper */ - private $beanHelper = NULL; + protected $beanHelper = NULL; /** * @var null */ - private $fetchType = NULL; + protected $fetchType = NULL; /** * @var string */ - private $withSql = ''; + protected $withSql = ''; /** * @var array */ - private $withParams = array(); + protected $withParams = array(); /** * @var string */ - private $aliasName = NULL; + protected $aliasName = NULL; /** * @var string */ - private $via = NULL; - + protected $via = NULL; + /** * @var boolean */ - private $writeOnly = false; + protected $noLoad = FALSE; - /** Returns the alias for a type - * - * @param string $type type - * - * @return string $type type + /** + * @var boolean */ - private function getAlias( $type ) - { - if ( $this->fetchType ) { - $type = $this->fetchType; - $this->fetchType = NULL; - } + protected $all = FALSE; - return $type; + /** + * Parses the join in the with-snippet. + * For instance: + * + * $author + * ->withCondition(' @joined.detail.title LIKE ? ') + * ->ownBookList; + * + * will automatically join 'detail' on book to + * access the title field. + * + * @note this feature requires Narrow Field Mode and Join Feature + * to be both activated (default). + * + * @param string $type the source type for the join + * + * @return string $joinSql + */ + private function parseJoin( $type ) + { + $joinSql = ''; + $joins = array(); + if ( strpos($this->withSql, '@joined.' ) !== FALSE ) { + $writer = $this->beanHelper->getToolBox()->getWriter(); + $oldParts = $parts = explode( '@joined.', $this->withSql ); + array_shift( $parts ); + foreach($parts as $part) { + $explosion = explode( '.', $part ); + $joinInfo = array_shift( $explosion ); + //Dont join more than once.. + if ( !isset( $joins[$joinInfo] ) ) { + $joins[ $joinInfo ] = true; + $joinSql .= $writer->writeJoin( $type, $joinInfo, 'LEFT' ); + } + } + $this->withSql = implode( '', $oldParts ); + $joinSql .= ' WHERE '; + } + return $joinSql; } /** @@ -747,6 +1131,7 @@ class RedBean_OODBBean implements IteratorAggregate, ArrayAccess, Countable */ private function getSharedList( $type, $redbean, $toolbox ) { + $writer = $toolbox->getWriter(); if ( $this->via ) { @@ -754,15 +1139,16 @@ class RedBean_OODBBean implements IteratorAggregate, ArrayAccess, Countable if ( $oldName !== $this->via ) { //set the new renaming rule $writer->renameAssocTable( $oldName, $this->via ); - $this->via = NULL; } + $this->via = NULL; } - $type = $this->beau( $type ); - - $assocManager = $redbean->getAssociationManager(); - - $beans = $assocManager->relatedSimple( $this, $type, $this->withSql, $this->withParams ); + $beans = array(); + if ($this->getID()) { + $type = $this->beau( $type ); + $assocManager = $redbean->getAssociationManager(); + $beans = $assocManager->related( $this, $type, $this->withSql, $this->withParams ); + } $this->withSql = ''; $this->withParams = array(); @@ -774,7 +1160,8 @@ class RedBean_OODBBean implements IteratorAggregate, ArrayAccess, Countable * Internal method. * Obtains the own list of a certain type. * - * @param string $type name of the list you want to retrieve + * @param string $type name of the list you want to retrieve + * @param OODB $oodb The RB OODB object database instance * * @return array */ @@ -796,7 +1183,7 @@ class RedBean_OODBBean implements IteratorAggregate, ArrayAccess, Countable $beans = array(); - if ( $this->getID() > 0 ) { + if ( $this->getID() ) { $firstKey = NULL; if ( count( $this->withParams ) > 0 ) { @@ -805,15 +1192,17 @@ class RedBean_OODBBean implements IteratorAggregate, ArrayAccess, Countable $firstKey = key( $this->withParams ); } + $joinSql = $this->parseJoin( $type ); + if ( !is_numeric( $firstKey ) || $firstKey === NULL ) { $bindings = $this->withParams; $bindings[':slot0'] = $this->getID(); - $beans = $redbean->find( $type, array(), " $myFieldLink = :slot0 " . $this->withSql, $bindings ); + $beans = $redbean->find( $type, array(), " {$joinSql} $myFieldLink = :slot0 " . $this->withSql, $bindings ); } else { $bindings = array_merge( array( $this->getID() ), $this->withParams ); - $beans = $redbean->find( $type, array(), " $myFieldLink = ? " . $this->withSql, $bindings ); + $beans = $redbean->find( $type, array(), " {$joinSql} $myFieldLink = ? " . $this->withSql, $bindings ); } } @@ -828,31 +1217,24 @@ class RedBean_OODBBean implements IteratorAggregate, ArrayAccess, Countable } /** - * By default own-lists and shared-lists no longer have IDs as keys (3.3+), - * this is because exportAll also does not offer this feature and we want the - * ORM to be more consistent. Also, exporting without keys makes it easier to - * export lists to Javascript because unlike in PHP in JS arrays will fill up gaps. + * Sets a meta property for all beans. This is a quicker way to set + * the meta properties for a collection of beans because this method + * can directly access the property arrays of the beans. + * This method returns the beans. * - * @param boolean $yesNo + * @param array $beans beans to set the meta property of + * @param string $property property to set + * @param mixed $value value * - * @return void + * @return array */ - public static function setFlagKeyedExport( $flag ) + public static function setMetaAll( $beans, $property, $value ) { - self::$flagKeyedExport = (boolean) $flag; - } + foreach( $beans as $bean ) { + $bean->__info[ $property ] = $value; + } - /** - * Flag indicates whether column names with CamelCase are supported and automatically - * converted; example: isForSale -> is_for_sale - * - * @param boolean - * - * @return void - */ - public static function setFlagBeautifulColumnNames( $flag ) - { - self::$flagUseBeautyCols = (boolean) $flag; + return $beans; } /** @@ -862,17 +1244,18 @@ class RedBean_OODBBean implements IteratorAggregate, ArrayAccess, Countable * if you build your own bean dispensing mechanism. * * @param string $type type of the new bean - * @param RedBean_BeanHelper $beanhelper bean helper to obtain a toolbox and a model + * @param BeanHelper $beanhelper bean helper to obtain a toolbox and a model * * @return void */ - public function initializeForDispense( $type, RedBean_BeanHelper $beanhelper ) + public function initializeForDispense( $type, BeanHelper $beanhelper ) { $this->beanHelper = $beanhelper; $this->__info['type'] = $type; $this->__info['sys.id'] = 'id'; $this->__info['sys.orig'] = array( 'id' => 0 ); $this->__info['tainted'] = TRUE; + $this->__info['changed'] = TRUE; $this->properties['id'] = 0; } @@ -883,30 +1266,30 @@ class RedBean_OODBBean implements IteratorAggregate, ArrayAccess, Countable * nested beans (bean lists: ownBean, sharedBean) without the need to * rely on static calls to the facade (or make this class dep. on OODB). * - * @param RedBean_BeanHelper $helper + * @param BeanHelper $helper * * @return void */ - public function setBeanHelper( RedBean_BeanHelper $helper ) + public function setBeanHelper( BeanHelper $helper ) { $this->beanHelper = $helper; } /** - * Returns an ArrayIterator so you can treat the bean like + * Returns an\ArrayIterator so you can treat the bean like * an array with the properties container as its contents. * This method is meant for PHP and allows you to access beans as if * they were arrays, i.e. using array notation: - * + * * $bean[ $key ] = $value; - * + * * Note that not all PHP functions work with the array interface. * - * @return ArrayIterator + * @return\ArrayIterator */ public function getIterator() { - return new ArrayIterator( $this->properties ); + return new\ArrayIterator( $this->properties ); } /** @@ -921,7 +1304,7 @@ class RedBean_OODBBean implements IteratorAggregate, ArrayAccess, Countable * @param string|array $selection selection of values * @param boolean $notrim if TRUE selection keys will NOT be trimmed * - * @return RedBean_OODBBean + * @return OODBBean */ public function import( $array, $selection = FALSE, $notrim = FALSE ) { @@ -938,7 +1321,25 @@ class RedBean_OODBBean implements IteratorAggregate, ArrayAccess, Countable foreach ( $array as $key => $value ) { if ( $key != '__info' ) { if ( !$selection || ( $selection && in_array( $key, $selection ) ) ) { - $this->$key = $value; + if ( is_array($value ) ) { + if ( isset( $value['_type'] ) ) { + $bean = $this->beanHelper->getToolbox()->getRedBean()->dispense( $value['_type'] ); + unset( $value['_type'] ); + $bean->import($value); + $this->$key = $bean; + } else { + $listBeans = array(); + foreach( $value as $listKey => $listItem ) { + $bean = $this->beanHelper->getToolbox()->getRedBean()->dispense( $listItem['_type'] ); + unset( $listItem['_type'] ); + $bean->import($listItem); + $list = &$this->$key; + $list[ $listKey ] = $bean; + } + } + } else { + $this->$key = $value; + } } } } @@ -947,8 +1348,8 @@ class RedBean_OODBBean implements IteratorAggregate, ArrayAccess, Countable } /** - * Fast way to import a database row, does not perform any checks but still - * sets original values in meta properties (also the fast way). + * Fast way to import a row. + * Does not perform any checks. * * @param array $row a database row * @@ -956,25 +1357,25 @@ class RedBean_OODBBean implements IteratorAggregate, ArrayAccess, Countable */ public function importRow( $row ) { - $this->properties = $row; + $this->properties = $row; $this->__info['sys.orig'] = $row; + $this->__info['changed'] = FALSE; return $this; } - /** * Imports data from another bean. Chainable. * Copies the properties from the source bean to the internal * property list. * - * @param RedBean_OODBBean $sourceBean the source bean to take properties from + * @param OODBBean $sourceBean the source bean to take properties from * - * @return RedBean_OODBBean + * @return OODBBean */ - public function importFrom( RedBean_OODBBean $sourceBean ) + public function importFrom( OODBBean $sourceBean ) { $this->__info['tainted'] = TRUE; - + $this->__info['changed'] = TRUE; $this->properties = $sourceBean->properties; return $this; @@ -985,11 +1386,11 @@ class RedBean_OODBBean implements IteratorAggregate, ArrayAccess, Countable * Just like import() but keeps the original ID. * Chainable. * - * @param RedBean_OODBBean $otherBean the bean whose properties you would like to copy + * @param OODBBean $otherBean the bean whose properties you would like to copy * - * @return RedBean_OODBBean + * @return OODBBean */ - public function inject( RedBean_OODBBean $otherBean ) + public function inject( OODBBean $otherBean ) { $myID = $this->properties['id']; @@ -1032,15 +1433,10 @@ class RedBean_OODBBean implements IteratorAggregate, ArrayAccess, Countable $vn = array(); foreach ( $value as $i => $b ) { - if ( is_numeric( $i ) && !self::$flagKeyedExport ) { - $vn[] = $b->export( $meta, FALSE, FALSE, $filters ); - } else { - $vn[$i] = $b->export( $meta, FALSE, FALSE, $filters ); - } - + $vn[] = $b->export( $meta, FALSE, FALSE, $filters ); $value = $vn; } - } elseif ( $value instanceof RedBean_OODBBean ) { + } elseif ( $value instanceof OODBBean ) { if ( $hasFilters ) { if ( !in_array( strtolower( $value->getMeta( 'type' ) ), $filters ) ) continue; } @@ -1058,24 +1454,6 @@ class RedBean_OODBBean implements IteratorAggregate, ArrayAccess, Countable return $arr; } - /** - * Exports the bean to an object. - * This method exports the bean data to the specified object. - * Only scalar values will be exported by this method. - * - * @param object $obj target object - * - * @return array - */ - public function exportToObj( $object ) - { - foreach ( $this->properties as $key => $value ) { - if ( is_scalar( $value ) ) { - $object->$key = $value; - } - } - } - /** * Implements isset() function for use as an array. * @@ -1085,6 +1463,11 @@ class RedBean_OODBBean implements IteratorAggregate, ArrayAccess, Countable */ public function __isset( $property ) { + $property = $this->beau( $property ); + + if ( strpos( $property, 'xown' ) === 0 && ctype_upper( substr( $property, 4, 1 ) ) ) { + $property = substr($property, 1); + } return isset( $this->properties[$property] ); } @@ -1108,33 +1491,27 @@ class RedBean_OODBBean implements IteratorAggregate, ArrayAccess, Countable */ public function __unset( $property ) { - $this->writeOnly = true; - $this->__get( $property ); - $this->writeOnly = false; + $property = $this->beau( $property ); - $fieldLink = $property . '_id'; - - if ( isset( $this->$fieldLink ) ) { - //wanna unset a bean reference? - $this->$fieldLink = NULL; + if ( strpos( $property, 'xown' ) === 0 && ctype_upper( substr( $property, 4, 1 ) ) ) { + $property = substr($property, 1); } - if ( ( isset( $this->properties[$property] ) ) ) { - unset( $this->properties[$property] ); - } - } - - /** - * Removes a property from the properties list without invoking - * an __unset on the bean. - * - * @param string $property property that needs to be unset - * - * @return void - */ - public function removeProperty( $property ) - { unset( $this->properties[$property] ); + + $shadowKey = 'sys.shadow.'.$property; + if ( isset( $this->__info[ $shadowKey ] ) ) unset( $this->__info[$shadowKey] ); + + //also clear modifiers + $this->withSql = ''; + $this->withParams = array(); + $this->aliasName = NULL; + $this->fetchType = NULL; + $this->noLoad = FALSE; + $this->all = FALSE; + $this->via = NULL; + + return; } /** @@ -1148,20 +1525,15 @@ class RedBean_OODBBean implements IteratorAggregate, ArrayAccess, Countable * the additional SQL snippet will be merged into the final * query. * - * @param string|RedBean_SQLHelper $sql SQL to be added to retrieval query. - * @param array $bindings array with parameters to bind to SQL snippet + * @param string $sql SQL to be added to retrieval query. + * @param array $bindings array with parameters to bind to SQL snippet * - * @return RedBean_OODBBean + * @return OODBBean */ public function with( $sql, $bindings = array() ) { - if ( $sql instanceof RedBean_SQLHelper ) { - list( $this->withSql, $this->withParams ) = $sql->getQuery(); - } else { - $this->withSql = $sql; - $this->withParams = $bindings; - } - + $this->withSql = $sql; + $this->withParams = $bindings; return $this; } @@ -1175,20 +1547,39 @@ class RedBean_OODBBean implements IteratorAggregate, ArrayAccess, Countable * * This will return in the own list only the pages having 'chapter == 3'. * - * @param string|RedBean_SQLHelper $sql SQL to be added to retrieval query (prefixed by AND) - * @param array $bindings array with parameters to bind to SQL snippet + * @param string $sql SQL to be added to retrieval query (prefixed by AND) + * @param array $bindings array with parameters to bind to SQL snippet * - * @return RedBean_OODBBean + * @return OODBBean */ public function withCondition( $sql, $bindings = array() ) { - if ( $sql instanceof RedBean_SQLHelper ) { - list( $sql, $bindings ) = $sql->getQuery(); - } - $this->withSql = ' AND ' . $sql; $this->withParams = $bindings; + return $this; + } + /** + * When prefix for a list, this causes the list to reload. + * + * @return self + */ + public function all() + { + $this->all = TRUE; + return $this; + } + + /** + * Tells the bean to only access the list but not load + * its contents. Use this if you only want to add something to a list + * and you have no interest in retrieving its contents from the database. + * + * @return self + */ + public function noLoad() + { + $this->noLoad = TRUE; return $this; } @@ -1215,7 +1606,7 @@ class RedBean_OODBBean implements IteratorAggregate, ArrayAccess, Countable * * @param string $aliasName the alias name to use * - * @return RedBean_OODBBean + * @return OODBBean */ public function alias( $aliasName ) { @@ -1237,6 +1628,21 @@ class RedBean_OODBBean implements IteratorAggregate, ArrayAccess, Countable return $this->properties; } + /** + * Returns properties of bean as an array. + * This method returns the raw internal property list of the + * bean. Only use this method for optimization purposes. Otherwise + * use the export() method to export bean data to arrays. + * This method returns an array with the properties array and + * the type (string). + * + * @return array + */ + public function getPropertiesAndType() + { + return array( $this->properties, $this->__info['type'] ); + } + /** * Turns a camelcase property name into an underscored property name. * Examples: @@ -1253,36 +1659,64 @@ class RedBean_OODBBean implements IteratorAggregate, ArrayAccess, Countable { static $beautifulColumns = array(); - if ( !self::$flagUseBeautyCols ) return $property; - if ( ctype_lower( $property ) ) return $property; if ( strpos( $property, 'own' ) === 0 + || strpos( $property, 'xown' ) === 0 || strpos( $property, 'shared' ) === 0 ) { + + $property = preg_replace( '/List$/', '', $property ); return $property; } if ( !isset( $beautifulColumns[$property] ) ) { - $beautifulColumns[$property] = strtolower( preg_replace( '/(?<=[a-z])([A-Z])|([A-Z])(?=[a-z])/', '_$1$2', $property ) ); + $beautifulColumns[$property] = AQueryWriter::camelsSnake( $property ); } return $beautifulColumns[$property]; } + + /** - * Clears state. - * Internal method. Clears the state of the query modifiers of the bean. - * Query modifiers are: with(), withCondition(), alias() and fetchAs(). - * - * @return void + * Clears all modifiers. + * + * @return self */ - private function clear() { + public function clearModifiers() + { $this->withSql = ''; $this->withParams = array(); $this->aliasName = NULL; $this->fetchType = NULL; + $this->noLoad = FALSE; + $this->all = FALSE; + $this->via = NULL; + return $this; + } + + /** + * Determines whether a list is opened in exclusive mode or not. + * If a list has been opened in exclusive mode this method will return TRUE, + * othwerwise it will return FALSE. + * + * @param string $listName name of the list to check + * + * @return boolean + */ + public function isListInExclusiveMode( $listName ) + { + $listName = $this->beau( $listName ); + + if ( strpos( $listName, 'xown' ) === 0 && ctype_upper( substr( $listName, 4, 1 ) ) ) { + $listName = substr($listName, 1); + } + + $listName = lcfirst( substr( $listName, 3 ) ); + + return ( isset( $this->__info['sys.exclusive-'.$listName] ) && $this->__info['sys.exclusive-'.$listName] ); } /** @@ -1298,102 +1732,116 @@ class RedBean_OODBBean implements IteratorAggregate, ArrayAccess, Countable */ public function &__get( $property ) { - if ( !$this->flagSkipBeau ) $property = $this->beau( $property ); + $isEx = FALSE; + $isOwn = FALSE; + $isShared = FALSE; - if ( $this->beanHelper ) { - list( $redbean, , , $toolbox ) = $this->beanHelper->getExtractedToolbox(); + if ( !ctype_lower( $property ) ) { + $property = $this->beau( $property ); + if ( strpos( $property, 'xown' ) === 0 && ctype_upper( substr( $property, 4, 1 ) ) ) { + $property = substr($property, 1); + $listName = lcfirst( substr( $property, 3 ) ); + $isEx = TRUE; + $isOwn = TRUE; + $this->__info['sys.exclusive-'.$listName] = TRUE; + } elseif ( strpos( $property, 'own' ) === 0 && ctype_upper( substr( $property, 3, 1 ) ) ) { + $isOwn = TRUE; + $listName = lcfirst( substr( $property, 3 ) ); + } elseif ( strpos( $property, 'shared' ) === 0 && ctype_upper( substr( $property, 6, 1 ) ) ) { + $isShared = TRUE; + } } - $isOwn = strpos( $property, 'own' ) === 0 && ctype_upper( substr( $property, 3, 1 ) ); - $isShared = strpos( $property, 'shared' ) === 0 && ctype_upper( substr( $property, 6, 1 ) ); + $fieldLink = $property . '_id'; + $exists = isset( $this->properties[$property] ); - if ($isOwn) $listName = lcfirst( substr( $property, 3 ) ); + //If not exists and no field link and no list, bail out. + if ( !$exists && !isset($this->$fieldLink) && (!$isOwn && !$isShared )) { - $hasAlias = (!is_null($this->aliasName)); + $this->withSql = ''; + $this->withParams = array(); + $this->aliasName = NULL; + $this->fetchType = NULL; + $this->noLoad = FALSE; + $this->all = FALSE; + $this->via = NULL; + $NULL = NULL; + return $NULL; + } + + $hasAlias = (!is_null($this->aliasName)); $differentAlias = ($hasAlias && $isOwn && isset($this->__info['sys.alias.'.$listName])) ? - ($this->__info['sys.alias.'.$listName] !== $this->aliasName) : FALSE; + ($this->__info['sys.alias.'.$listName] !== $this->aliasName) : FALSE; + $hasSQL = ($this->withSql !== '' || $this->via !== NULL); + $hasAll = (boolean) ($this->all); - $hasSQL = ($this->withSql !== '' || $this->via !== null); - - $exists = isset( $this->properties[$property] ); - - if ($exists && !$isOwn && !$isShared) { - - $this->clear(); + //If exists and no list or exits and list not changed, bail out. + if ( $exists && ((!$isOwn && !$isShared ) || (!$hasSQL && !$differentAlias && !$hasAll)) ) { + $this->withSql = ''; + $this->withParams = array(); + $this->aliasName = NULL; + $this->fetchType = NULL; + $this->noLoad = FALSE; + $this->all = FALSE; + $this->via = NULL; return $this->properties[$property]; } - if ($exists && !$hasSQL && !$differentAlias) { - $this->clear(); + list( $redbean, , , $toolbox ) = $this->beanHelper->getExtractedToolbox(); - return $this->properties[$property]; - } - - $fieldLink = $property . '_id'; - if ( isset( $this->$fieldLink ) && $fieldLink !== $this->getMeta( 'sys.idfield' ) ) { + if ( isset( $this->$fieldLink ) ) { $this->__info['tainted'] = TRUE; - $bean = NULL; if ( isset( $this->__info["sys.parentcache.$property"] ) ) { $bean = $this->__info["sys.parentcache.$property"]; - } - - if ( $this->writeOnly ) { - $this->clear(); - $NULL = null; - return $NULL; - } - - if ( !$bean ) { - $type = $this->getAlias( $property ); - - if ( $this->withSql !== '' ) { - - $beans = $redbean->find( - $type, - array( 'id' => array( $this->properties[$fieldLink] ) ), - $this->withSql, $this->withParams ); - - $bean = ( empty( $beans ) ) ? NULL : reset( $beans ); - $this->withSql = ''; - $this->withParams = ''; + } else { + if ( $this->fetchType ) { + $type = $this->fetchType; + $this->fetchType = NULL; } else { - $bean = $redbean->load( $type, $this->properties[$fieldLink] ); + $type = $property; } + $bean = $redbean->load( $type, $this->properties[$fieldLink] ); } $this->properties[$property] = $bean; - $this->clear(); + $this->withSql = ''; + $this->withParams = array(); + $this->aliasName = NULL; + $this->fetchType = NULL; + $this->noLoad = FALSE; + $this->all = FALSE; + $this->via = NULL; return $this->properties[$property]; + + } + //Implicit: elseif ( $isOwn || $isShared ) { + if ( $this->noLoad ) { + $beans = array(); + } elseif ( $isOwn ) { + $beans = $this->getOwnList( $listName, $redbean ); + } else { + $beans = $this->getSharedList( lcfirst( substr( $property, 6 ) ), $redbean, $toolbox ); } - if ( $isOwn || $isShared ) { - if ( $isOwn ) { - $beans = $this->getOwnList( $listName, $redbean ); - } else { - $beans = $this->getSharedList( lcfirst( substr( $property, 6 ) ), $redbean, $toolbox ); - } + $this->properties[$property] = $beans; + $this->__info["sys.shadow.$property"] = $beans; + $this->__info['tainted'] = TRUE; - $this->properties[$property] = $beans; + $this->withSql = ''; + $this->withParams = array(); + $this->aliasName = NULL; + $this->fetchType = NULL; + $this->noLoad = FALSE; + $this->all = FALSE; + $this->via = NULL; - $this->__info["sys.shadow.$property"] = $beans; - $this->__info['tainted'] = TRUE; - - $this->clear(); - - return $this->properties[$property]; - } - - $this->clear(); - - $NULL = NULL; - - return $NULL; + return $this->properties[$property]; } /** @@ -1408,31 +1856,70 @@ class RedBean_OODBBean implements IteratorAggregate, ArrayAccess, Countable * * @return void * - * @throws RedBean_Exception_Security + * @throws Security */ public function __set( $property, $value ) { - $property = $this->beau( $property ); + $isEx = FALSE; + $isOwn = FALSE; + $isShared = FALSE; - $this->flagSkipBeau = TRUE; + if ( !ctype_lower( $property ) ) { + $property = $this->beau( $property ); + if ( strpos( $property, 'xown' ) === 0 && ctype_upper( substr( $property, 4, 1 ) ) ) { + $property = substr($property, 1); + $listName = lcfirst( substr( $property, 3 ) ); + $isEx = TRUE; + $isOwn = TRUE; + $this->__info['sys.exclusive-'.$listName] = TRUE; + } elseif ( strpos( $property, 'own' ) === 0 && ctype_upper( substr( $property, 3, 1 ) ) ) { + $isOwn = TRUE; + $listName = lcfirst( substr( $property, 3 ) ); + } elseif ( strpos( $property, 'shared' ) === 0 && ctype_upper( substr( $property, 6, 1 ) ) ) { + $isShared = TRUE; + } + } - $this->writeOnly = true; - $this->__get( $property ); - $this->writeOnly = false; + $hasAlias = (!is_null($this->aliasName)); + $differentAlias = ($hasAlias && $isOwn && isset($this->__info['sys.alias.'.$listName])) ? + ($this->__info['sys.alias.'.$listName] !== $this->aliasName) : FALSE; + $hasSQL = ($this->withSql !== '' || $this->via !== NULL); + $exists = isset( $this->properties[$property] ); + $fieldLink = $property . '_id'; - $this->flagSkipBeau = FALSE; + if ( ($isOwn || $isShared) && (!$exists || $hasSQL || $differentAlias) ) { - $this->setMeta( 'tainted', TRUE ); + if ( !$this->noLoad ) { + list( $redbean, , , $toolbox ) = $this->beanHelper->getExtractedToolbox(); + if ( $isOwn ) { + $beans = $this->getOwnList( $listName, $redbean ); + } else { + $beans = $this->getSharedList( lcfirst( substr( $property, 6 ) ), $redbean, $toolbox ); + } + $this->__info["sys.shadow.$property"] = $beans; + } + } - if (isset( $this->properties[$property.'_id'] ) - && !( $value instanceof RedBean_OODBBean ) - ) { + $this->withSql = ''; + $this->withParams = array(); + $this->aliasName = NULL; + $this->fetchType = NULL; + $this->noLoad = FALSE; + $this->all = FALSE; + $this->via = NULL; + + $this->__info['tainted'] = TRUE; + $this->__info['changed'] = TRUE; + + if ( array_key_exists( $fieldLink, $this->properties ) && !( $value instanceof OODBBean ) ) { if ( is_null( $value ) || $value === FALSE ) { - $this->__unset( $property ); + + unset( $this->properties[ $property ]); + $this->properties[ $fieldLink ] = NULL; return; } else { - throw new RedBean_Exception_Security( 'Cannot cast to bean.' ); + throw new RedException( 'Cannot cast to bean.' ); } } @@ -1440,7 +1927,7 @@ class RedBean_OODBBean implements IteratorAggregate, ArrayAccess, Countable $value = '0'; } elseif ( $value === TRUE ) { $value = '1'; - } elseif ( $value instanceof DateTime ) { + } elseif ( $value instanceof \DateTime ) { $value = $value->format( 'Y-m-d H:i:s' ); } @@ -1467,6 +1954,7 @@ class RedBean_OODBBean implements IteratorAggregate, ArrayAccess, Countable if ( $taint ) { $this->__info['tainted'] = TRUE; + $this->__info['changed'] = TRUE; } } @@ -1501,7 +1989,7 @@ class RedBean_OODBBean implements IteratorAggregate, ArrayAccess, Countable * @param string $path path * @param mixed $value value * - * @return RedBean_OODBBean + * @return OODBBean */ public function setMeta( $path, $value ) { @@ -1515,11 +2003,11 @@ class RedBean_OODBBean implements IteratorAggregate, ArrayAccess, Countable * This is a convenience method to enable you to * exchange meta information easily. * - * @param RedBean_OODBBean $bean + * @param OODBBean $bean * - * @return RedBean_OODBBean + * @return OODBBean */ - public function copyMetaFrom( RedBean_OODBBean $bean ) + public function copyMetaFrom( OODBBean $bean ) { $this->__info = $bean->__info; @@ -1555,7 +2043,7 @@ class RedBean_OODBBean implements IteratorAggregate, ArrayAccess, Countable /** * Implementation of __toString Method * Routes call to Model. If the model implements a __toString() method this - * method will be called and the result will be returned. In case of an + * method will be called and the result will be returned. In case of an * echo-statement this result will be printed. If the model does not * implement a __toString method, this method will return a JSON * representation of the current bean. @@ -1592,13 +2080,17 @@ class RedBean_OODBBean implements IteratorAggregate, ArrayAccess, Countable * Implementation of Array Access Interface, you can access bean objects * like an array. * + * Array functions do not reveal x-own-lists and list-alias because + * you dont want duplicate entries in foreach-loops. + * Also offers a slight performance improvement for array access. + * * @param mixed $offset property * * @return boolean */ public function offsetExists( $offset ) { - return isset( $this->properties[$offset] ); + return $this->__isset( $offset ); } /** @@ -1606,13 +2098,17 @@ class RedBean_OODBBean implements IteratorAggregate, ArrayAccess, Countable * like an array. * Unsets a value from the array/bean. * + * Array functions do not reveal x-own-lists and list-alias because + * you dont want duplicate entries in foreach-loops. + * Also offers a slight performance improvement for array access. + * * @param mixed $offset property * * @return void */ public function offsetUnset( $offset ) { - unset( $this->properties[$offset] ); + $this->__unset( $offset ); } /** @@ -1620,11 +2116,15 @@ class RedBean_OODBBean implements IteratorAggregate, ArrayAccess, Countable * like an array. * Returns value of a property. * + * Array functions do not reveal x-own-lists and list-alias because + * you dont want duplicate entries in foreach-loops. + * Also offers a slight performance improvement for array access. + * * @param mixed $offset property * * @return mixed */ - public function offsetGet( $offset ) + public function &offsetGet( $offset ) { return $this->__get( $offset ); } @@ -1636,7 +2136,7 @@ class RedBean_OODBBean implements IteratorAggregate, ArrayAccess, Countable * * @param string $type preferred fetch type * - * @return RedBean_OODBBean + * @return OODBBean */ public function fetchAs( $type ) { @@ -1651,43 +2151,34 @@ class RedBean_OODBBean implements IteratorAggregate, ArrayAccess, Countable * * @param string $column * - * @return RedBean_OODBBean + * @return OODBBean */ public function poly( $field ) { return $this->fetchAs( $this->$field ); } - /** - * Treats the bean like a node in a tree and searches for all - * nested or parent beans. + * Traverses a bean property with the specified function. + * Recursively iterates through the property invoking the + * function for each bean along the way passing the bean to it. * - * To get all parent pages of a page: + * Can be used together with with, withCondition, alias and fetchAs. * - * $parentPages = $page->searchIn('page'); + * @param string $property property + * @param closure $function function * - * To get all child pages: - * - * $pages = $parentPage->searchIn('ownPage'); - * - * When searching in lists you can use SQL snippets in withCondition(): - * - * $pages = $parentPage - * ->withCondition(' rank = ? ', array($rank)) - * ->searchIn('ownPage'); - * - * Also works with alias() and fetchAs(). - * Note that shared lists are NOT supported. - * - * @param string $property property/list to search - * - * @return array + * @return OODBBean */ - public function searchIn($property) + public function traverse( $property, $function, $maxDepth = NULL ) { - if ( strpos( $property, 'shared' ) === 0 && ctype_upper( substr( $property, 6, 1 ) ) ) { - throw new RedBean_Exception_Security( 'Cannot search a shared list recursively.' ); + $this->via = NULL; + if ( strpos( $property, 'shared' ) !== FALSE ) { + throw new RedException( 'Traverse only works with (x)own-lists.' ); + } + + if ( !is_null( $maxDepth ) ) { + if ( !$maxDepth-- ) return $this; } $oldFetchType = $this->fetchType; @@ -1695,50 +2186,29 @@ class RedBean_OODBBean implements IteratorAggregate, ArrayAccess, Countable $oldWith = $this->withSql; $oldBindings = $this->withParams; - unset( $this->__info["sys.parentcache.$property"] ); + $beans = $this->$property; - $beanOrBeans = $this->$property; + if ( $beans === NULL ) return $this; - if ( $beanOrBeans instanceof RedBean_OODBBean ) { - $bean = $beanOrBeans; - $key = $bean->properties['id']; - $beans = array( $key => $bean ); - } elseif ( is_null( $beanOrBeans ) ) { - $beans = array(); - } else { - $beans = $beanOrBeans; - } + if ( !is_array( $beans ) ) $beans = array( $beans ); - unset( $this->properties[$property] ); - unset( $this->__info["sys.shadow.$property"] ); + foreach( $beans as $bean ) { - if ( $oldWith === '' ) { - $ufbeans = $beans; - } else { - $this->fetchType = $oldFetchType; - $this->aliasName = $oldAliasName; - $ufbeans = $this->$property; + $function( $bean ); - if ( is_null( $ufbeans ) ) $ufbeans = array(); - if ( $ufbeans instanceof RedBean_OODBBean ) $ufbeans = array( $ufbeans ); - } - - foreach( $ufbeans as $bean ) { $bean->fetchType = $oldFetchType; $bean->aliasName = $oldAliasName; $bean->withSql = $oldWith; $bean->withParams = $oldBindings; - $newBeans = $bean->searchIn( $property ); - - $beans = array_replace( $beans, $newBeans ); + $bean->traverse( $property, $function, $maxDepth ); } - return $beans; + return $this; } /** - * Implementation of Countable interface. Makes it possible to use + * Implementation of\Countable interface. Makes it possible to use * count() function on a bean. * * @return integer @@ -1776,7 +2246,7 @@ class RedBean_OODBBean implements IteratorAggregate, ArrayAccess, Countable * @param string $property the property of the bean * @param mixed $value the value you want to set * - * @return RedBean_OODBBean + * @return OODBBean */ public function setAttr( $property, $value ) { @@ -1791,7 +2261,7 @@ class RedBean_OODBBean implements IteratorAggregate, ArrayAccess, Countable * * @param array $properties properties you want to unset. * - * @return RedBean_OODBBean + * @return OODBBean */ public function unsetAll( $properties ) { @@ -1874,14 +2344,17 @@ class RedBean_OODBBean implements IteratorAggregate, ArrayAccess, Countable * example #1. After preparing the linking bean, the bean is returned thus * allowing the chained setter: ->song = $song. * - * @param string|RedBean_OODBBean $type type of bean to dispense or the full bean + * @param string|OODBBean $type type of bean to dispense or the full bean * @param string|array $qualification JSON string or array (optional) * - * @return RedBean_OODBBean + * @return OODBBean */ public function link( $typeOrBean, $qualification = array() ) { if ( is_string( $typeOrBean ) ) { + + $typeOrBean = AQueryWriter::camelsSnake( $typeOrBean ); + $bean = $this->beanHelper->getToolBox()->getRedBean()->dispense( $typeOrBean ); if ( is_string( $qualification ) ) { @@ -1907,7 +2380,7 @@ class RedBean_OODBBean implements IteratorAggregate, ArrayAccess, Countable /** * Returns the same bean freshly loaded from the database. * - * @return RedBean_OODBBean + * @return OODBBean */ public function fresh() { @@ -1919,11 +2392,11 @@ class RedBean_OODBBean implements IteratorAggregate, ArrayAccess, Countable * * @param string $via type you wish to use for shared lists * - * @return RedBean_OODBBean + * @return OODBBean */ public function via( $via ) { - $this->via = $via; + $this->via = AQueryWriter::camelsSnake( $via ); return $this; } @@ -1950,7 +2423,7 @@ class RedBean_OODBBean implements IteratorAggregate, ArrayAccess, Countable $count = 0; - if ( $this->getID() !== 0 ) { + if ( $this->getID() ) { $firstKey = NULL; if ( count( $this->withParams ) > 0 ) { @@ -1969,9 +2442,7 @@ class RedBean_OODBBean implements IteratorAggregate, ArrayAccess, Countable } - $this->withSql = ''; - $this->withParams = array(); - + $this->clearModifiers(); return (int) $count; } @@ -2002,33 +2473,108 @@ class RedBean_OODBBean implements IteratorAggregate, ArrayAccess, Countable $type = $this->beau( $type ); $count = 0; - if ( $this->getID() > 0 ) { + if ( $this->getID() ) { $count = $redbean->getAssociationManager()->relatedCount( $this, $type, $this->withSql, $this->withParams, TRUE ); } - $this->withSql = ''; - $this->withParams = array(); - + $this->clearModifiers(); return (integer) $count; } + /** + * Iterates through the specified own-list and + * fetches all properties (with their type) and + * returns the references. + * Use this method to quickly load indirectly related + * beans in an own-list. Whenever you cannot use a + * shared-list this method offers the same convenience + * by aggregating the parent beans of all children in + * the specified own-list. + * + * Example: + * + * $quest->aggr( 'xownQuestTarget', 'target', 'quest' ); + * + * Loads (in batch) and returns references to all + * quest beans residing in the $questTarget->target properties + * of each element in the xownQuestTargetList. + * + * @param string $list the list you wish to process + * @param string $property the property to load + * @param string $type the type of bean residing in this property (optional) + * + * @return array + */ + public function &aggr( $list, $property, $type = NULL ) + { + $this->via = NULL; + $ids = $beanIndex = $references = array(); + + if ( strlen( $list ) < 4 ) throw new RedException('Invalid own-list.'); + if ( strpos( $list, 'own') !== 0 ) throw new RedException('Only own-lists can be aggregated.'); + if ( !ctype_upper( substr( $list, 3, 1 ) ) ) throw new RedException('Invalid own-list.'); + + if ( is_null( $type ) ) $type = $property; + + foreach( $this->$list as $bean ) { + $field = $property . '_id'; + if ( isset( $bean->$field) ) { + $ids[] = $bean->$field; + $beanIndex[$bean->$field] = $bean; + } + } + + $beans = $this->beanHelper->getToolBox()->getRedBean()->batch( $type, $ids ); + + //now preload the beans as well + foreach( $beans as $bean ) { + $beanIndex[$bean->id]->setProperty( $property, $bean ); + } + + foreach( $beanIndex as $indexedBean ) { + $references[] = $indexedBean->$property; + } + + return $references; + } + /** * Tests whether the database identities of two beans are equal. * - * @param RedBean_OODBBean $bean other bean + * @param OODBBean $bean other bean * * @return boolean */ - public function equals(RedBean_OODBBean $bean) { + public function equals(OODBBean $bean) + { return (bool) ( ( (string) $this->properties['id'] === (string) $bean->properties['id'] ) && ( (string) $this->__info['type'] === (string) $bean->__info['type'] ) ); } + + } +} +namespace RedBeanPHP { -abstract class RedBean_Observable { //bracket must be here - otherwise coverage software does not understand. +use RedBeanPHP\Observer as Observer; + +/** + * Observable + * Base class for Observables + * + * @file RedBean/Observable.php + * @description Part of the observer pattern in RedBean + * @author Gabor de Mooij and the RedBeanPHP community + * @license BSD/GPLv2 + * + * copyright (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community. + * This source file is subject to the BSD/GPLv2 License that is bundled + * with this source code in the file license.txt. + */ +abstract class Observable { //bracket must be here - otherwise coverage software does not understand. /** * @var array @@ -2043,11 +2589,11 @@ abstract class RedBean_Observable { //bracket must be here - otherwise coverage * the event occurs. * * @param string $eventname event identifier - * @param RedBean_Observer $observer observer instance + * @param Observer $observer observer instance * * @return void */ - public function addEventListener( $eventname, RedBean_Observer $observer ) + public function addEventListener( $eventname, Observer $observer ) { if ( !isset( $this->observers[$eventname] ) ) { $this->observers[$eventname] = array(); @@ -2084,9 +2630,25 @@ abstract class RedBean_Observable { //bracket must be here - otherwise coverage } } } +} +namespace RedBeanPHP { -interface RedBean_Observer +/** + * Observer + * Interface for Observer object. Implementation of the + * observer pattern. + * + * @file RedBean/Observer.php + * @desc Part of the observer pattern in RedBean + * @author Gabor de Mooijand the RedBeanPHP community + * @license BSD/GPLv2 + * + * copyright (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community. + * This source file is subject to the BSD/GPLv2 License that is bundled + * with this source code in the file license.txt. + */ +interface Observer { /** @@ -2102,9 +2664,23 @@ interface RedBean_Observer */ public function onEvent( $eventname, $bean ); } +} +namespace RedBeanPHP { -interface RedBean_Adapter +/** + * Adapter Interface + * + * @file RedBean/Adapter.php + * @desc Describes the API for a RedBean Database Adapter. + * @author Gabor de Mooij and the RedBeanPHP Community + * @license BSD/GPLv2 + * + * (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community. + * This source file is subject to the BSD/GPLv2 License that is bundled + * with this source code in the file license.txt. + */ +interface Adapter { /** @@ -2263,13 +2839,33 @@ interface RedBean_Adapter */ public function close(); } +} +namespace RedBeanPHP\Adapter { -class RedBean_Adapter_DBAdapter extends RedBean_Observable implements RedBean_Adapter +use RedBeanPHP\Observable as Observable; +use RedBeanPHP\Adapter as Adapter; +use RedBeanPHP\Driver as Driver; + +/** + * DBAdapter (Database Adapter) + * + * @file RedBean/Adapter/DBAdapter.php + * @desc An adapter class to connect various database systems to RedBean + * @author Gabor de Mooij and the RedBeanPHP Community. + * @license BSD/GPLv2 + * + * Database Adapter Class. + * + * (c) copyright G.J.G.T. (Gabor) de Mooij and the RedBeanPHP community. + * This source file is subject to the BSD/GPLv2 License that is bundled + * with this source code in the file license.txt. + */ +class DBAdapter extends Observable implements Adapter { /** - * @var RedBean_Driver + * @var Driver */ private $db = NULL; @@ -2285,7 +2881,7 @@ class RedBean_Adapter_DBAdapter extends RedBean_Observable implements RedBean_Ad * This class provides an interface for RedBean to work * with ADO compatible DB instances. * - * @param RedBean_Driver $database ADO Compatible DB Instance + * @param Driver $database ADO Compatible DB Instance */ public function __construct( $database ) { @@ -2293,7 +2889,7 @@ class RedBean_Adapter_DBAdapter extends RedBean_Observable implements RedBean_Ad } /** - * @see RedBean_Adapter::getSQL + * @see Adapter::getSQL */ public function getSQL() { @@ -2301,7 +2897,7 @@ class RedBean_Adapter_DBAdapter extends RedBean_Observable implements RedBean_Ad } /** - * @see RedBean_Adapter::exec + * @see Adapter::exec */ public function exec( $sql, $bindings = array(), $noevent = FALSE ) { @@ -2314,7 +2910,7 @@ class RedBean_Adapter_DBAdapter extends RedBean_Observable implements RedBean_Ad } /** - * @see RedBean_Adapter::get + * @see Adapter::get */ public function get( $sql, $bindings = array() ) { @@ -2325,7 +2921,7 @@ class RedBean_Adapter_DBAdapter extends RedBean_Observable implements RedBean_Ad } /** - * @see RedBean_Adapter::getRow + * @see Adapter::getRow */ public function getRow( $sql, $bindings = array() ) { @@ -2336,7 +2932,7 @@ class RedBean_Adapter_DBAdapter extends RedBean_Observable implements RedBean_Ad } /** - * @see RedBean_Adapter::getCol + * @see Adapter::getCol */ public function getCol( $sql, $bindings = array() ) { @@ -2347,7 +2943,7 @@ class RedBean_Adapter_DBAdapter extends RedBean_Observable implements RedBean_Ad } /** - * @see RedBean_Adapter::getAssoc + * @see Adapter::getAssoc */ public function getAssoc( $sql, $bindings = array() ) { @@ -2391,7 +2987,7 @@ class RedBean_Adapter_DBAdapter extends RedBean_Observable implements RedBean_Ad } /** - * @see RedBean_Adapter::getCell + * @see Adapter::getCell */ public function getCell( $sql, $bindings = array(), $noSignal = NULL ) { @@ -2409,7 +3005,7 @@ class RedBean_Adapter_DBAdapter extends RedBean_Observable implements RedBean_Ad } /** - * @see RedBean_Adapter::getInsertID + * @see Adapter::getInsertID */ public function getInsertID() { @@ -2417,7 +3013,7 @@ class RedBean_Adapter_DBAdapter extends RedBean_Observable implements RedBean_Ad } /** - * @see RedBean_Adapter::getAffectedRows + * @see Adapter::getAffectedRows */ public function getAffectedRows() { @@ -2425,7 +3021,7 @@ class RedBean_Adapter_DBAdapter extends RedBean_Observable implements RedBean_Ad } /** - * @see RedBean_Adapter::getDatabase + * @see Adapter::getDatabase */ public function getDatabase() { @@ -2433,7 +3029,7 @@ class RedBean_Adapter_DBAdapter extends RedBean_Observable implements RedBean_Ad } /** - * @see RedBean_Adapter::startTransaction + * @see Adapter::startTransaction */ public function startTransaction() { @@ -2441,7 +3037,7 @@ class RedBean_Adapter_DBAdapter extends RedBean_Observable implements RedBean_Ad } /** - * @see RedBean_Adapter::commit + * @see Adapter::commit */ public function commit() { @@ -2449,7 +3045,7 @@ class RedBean_Adapter_DBAdapter extends RedBean_Observable implements RedBean_Ad } /** - * @see RedBean_Adapter::rollback + * @see Adapter::rollback */ public function rollback() { @@ -2457,17 +3053,46 @@ class RedBean_Adapter_DBAdapter extends RedBean_Observable implements RedBean_Ad } /** - * @see RedBean_Adapter::close. + * @see Adapter::close. */ public function close() { $this->db->close(); } } +} +namespace RedBeanPHP { -interface RedBean_QueryWriter +/** + * QueryWriter + * Interface for QueryWriters + * + * @file RedBean/QueryWriter.php + * @desc Describes the API for a QueryWriter + * @author Gabor de Mooij and the RedBeanPHP community + * @license BSD/GPLv2 + * + * Notes: + * - Whenever you see a parameter called $table or $type you should always + * be aware of the fact that this argument contains a Bean Type string, not the + * actual table name. These raw type names are passed to safeTable() to obtain the + * actual name of the database table. Don't let the names confuse you $type/$table + * refers to Bean Type, not physical database table names! + * - This is the interface for FLUID database drivers. Drivers intended to support + * just FROZEN mode should implement the IceWriter instead. + * + * copyright (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community. + * This source file is subject to the BSD/GPLv2 License that is bundled + * with this source code in the file license.txt. + */ +interface QueryWriter { + /** + * SQL filter constants + */ + const C_SQLFILTER_READ = 'r'; + const C_SQLFILTER_WRITE = 'w'; /** * Query Writer constants. @@ -2490,43 +3115,65 @@ interface RedBean_QueryWriter * Define GLUE types for use with glueSQLCondition methods. * Determines how to prefix a snippet of SQL before appending it * to other SQL (or integrating it, mixing it otherwise). - * + * * WHERE - glue as WHERE condition * AND - glue as AND condition */ const C_GLUE_WHERE = 1; const C_GLUE_AND = 2; + /** + * Writes an SQL Snippet for a JOIN, returns the + * SQL snippet string. + * + * @param string $type source type + * @param string $targetType target type (type to join) + * @param string $leftRight type of join (possible: 'LEFT', 'RIGHT' or 'INNER'). + * + * @return string $joinSQLSnippet + */ + public function writeJoin( $type, $targetType, $joinType ); + /** * Glues an SQL snippet to the beginning of a WHERE clause. * This ensures users don't have to add WHERE to their query snippets. * - * The snippet gets prefixed with WHERE or AND + * The snippet gets prefixed with WHERE or AND * if it starts with a condition. - * + * * If the snippet does NOT start with a condition (or this function thinks so) * the snippet is returned as-is. - * + * * The GLUE type determines the prefix: - * + * * - NONE prefixes with WHERE - * - WHERE prefixes with WHERE and replaces AND if snippets starts with AND + * - WHERE prefixes with WHERE and replaces AND if snippets starts with AND * - AND prefixes with AND - * + * * This method will never replace WHERE with AND since a snippet should never * begin with WHERE in the first place. OR is not supported. - * + * * Only a limited set of clauses will be recognized as non-conditions. * For instance beginning a snippet with complex statements like JOIN or UNION * will not work. This is too complex for use in a snippet. * * @param string $sql SQL Snippet * @param integer $glue the GLUE type - how to glue (C_GLUE_WHERE or C_GLUE_AND) - * + * * @return string */ public function glueSQLCondition( $sql, $glue = NULL ); + /** + * Determines if there is a LIMIT 1 clause in the SQL. + * If not, it will add a LIMIT 1. (used for findOne). + * + * @param string $sql query to scan and adjust + * + * @return string + */ + public function glueLimitOne( $sql ); + /** * Returns the tables that are in the database. * @@ -2635,20 +3282,6 @@ interface RedBean_QueryWriter */ public function queryRecordRelated( $sourceType, $destType, $linkID, $addSql = '', $bindings = array() ); - /** - * Returns linking records. This method is used to obtain records using a link table and - * allows the SQL snippets to reference columns in the link table for additional filtering or ordering. - * - * @param string $sourceType source type, the reference type you want to use to fetch related items on the other side - * @param string $destType destination type, the target type you want to get beans of - * @param mixed $linkID ID to use for the link table - * @param string $addSql Additional SQL snippet - * @param array $bindings Bindings for SQL snippet - * - * @return array - */ - public function queryRecordLinks( $sourceType, $destType, $linkIDs, $addSql = '', $bindings = array() ); - /** * Returns the row that links $sourceType $sourcID to $destType $destID in an N-M relation. * @@ -2687,6 +3320,23 @@ interface RedBean_QueryWriter */ public function queryRecordCountRelated( $sourceType, $targetType, $linkID, $addSQL = '', $bindings = array() ); + /** + * Returns all rows of specified type that have been tagged with one of the + * strings in the specified tag list array. + * + * Note that the additional SQL snippet can only be used for pagination, + * the SQL snippet will be appended to the end of the query. + * + * @param string $type the bean type you want to query + * @param array $tagList an array of strings, each string containing a tag title + * @param boolean $all if TRUE only return records that have been associated with ALL the tags in the list + * @param string $addSql addition SQL snippet, for pagination + * @param array $bindings parameter bindings for additional SQL snippet + * + * @return array + */ + public function queryTagged( $type, $tagList, $all = FALSE, $addSql = '', $bindings = array() ); + /** * This method should update (or insert a record), it takes * a table name, a list of update values ( $field => $value ) and an @@ -2782,14 +3432,15 @@ interface RedBean_QueryWriter * This methods accepts a type and infers the corresponding table name. * * - * @param string $type type that will have a foreign key field - * @param string $targetType points to this type - * @param string $field field that contains the foreign key value - * @param string $targetField field where the fk points to + * @param string $type type that will have a foreign key field + * @param string $targetType points to this type + * @param string $field field that contains the foreign key value + * @param string $targetField field where the fk points to + * @param string $isDep whether target is dependent and should cascade on update/delete * * @return void */ - public function addFK( $type, $targetType, $field, $targetField ); + public function addFK( $type, $targetType, $field, $targetField, $isDep = false ); /** * This method will add an index to a type and field with name @@ -2860,11 +3511,35 @@ interface RedBean_QueryWriter */ public function getAssocTable( $types ); } +} +namespace RedBeanPHP\QueryWriter { -abstract class RedBean_QueryWriter_AQueryWriter { //bracket must be here - otherwise coverage software does not understand. +use RedBeanPHP\Adapter\DBAdapter as DBAdapter; +use RedBeanPHP\RedException as RedException; +use RedBeanPHP\QueryWriter as QueryWriter; +use RedBeanPHP\OODBBean as OODBBean; + +/** + * RedBean Abstract Query Writer + * + * @file RedBean/QueryWriter/AQueryWriter.php + * @desc Query Writer (abstract class) + * @author Gabor de Mooij and the RedBeanPHP Community + * @license BSD/GPLv2 + * + * Represents an abstract Database to RedBean + * To write a driver for a different database for RedBean + * Contains a number of functions all implementors can + * inherit or override. + * + * (c) copyright G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community. + * This source file is subject to the BSD/GPLv2 License that is bundled + * with this source code in the file license.txt. + */ +abstract class AQueryWriter { //bracket must be here - otherwise coverage software does not understand. /** - * @var RedBean_Adapter_DBAdapter + * @var DBAdapter */ protected $adapter; @@ -2881,7 +3556,7 @@ abstract class RedBean_QueryWriter_AQueryWriter { //bracket must be here - other /** * @var boolean */ - protected $flagUseCache = FALSE; + protected $flagUseCache = TRUE; /** * @var array @@ -2891,13 +3566,137 @@ abstract class RedBean_QueryWriter_AQueryWriter { //bracket must be here - other /** * @var array */ - protected static $renames = array(); + public static $renames = array(); + + /** + * @var array + */ + private static $sqlFilters = array(); + + /** + * @var boolean + */ + private static $flagSQLFilterSafeMode = false; + + /** + * @var boolean + */ + private static $flagNarrowFieldMode = true; /** * @var array */ public $typeno_sqltype = array(); + /** + * Clears renames. + * + * @return void + */ + public static function clearRenames() + { + self::$renames = array(); + } + + /** + * Toggles 'Narrow Field Mode'. + * In Narrow Field mode the queryRecord method will + * narrow its selection field to + * + * SELECT {table}.* + * + * instead of + * + * SELECT * + * + * This is a better way of querying because it allows + * more flexibility (for instance joins). However if you need + * the wide selector for backward compatibility; use this method + * to turn OFF Narrow Field Mode by passing FALSE. + * + * @param boolean $narrowField TRUE = Narrow Field FALSE = Wide Field + * + * @return void + */ + public static function setNarrowFieldMode( $narrowField ) + { + self::$flagNarrowFieldMode = (boolean) $narrowField; + } + + /** + * Sets SQL filters. + * This is a lowlevel method to set the SQL filter array. + * The format of this array is: + * + * array( + * '' => array( + * '' => array( + * '' => '' + * ) + * ) + * ) + * + * Example: + * + * array( + * QueryWriter::C_SQLFILTER_READ => array( + * 'book' => array( + * 'title' => ' LOWER(book.title) ' + * ) + * ) + * + * Note that you can use constants instead of magical chars + * as keys for the uppermost array. + * This is a lowlevel method. For a more friendly method + * please take a look at the facade: R::bindFunc(). + * + * @param array + */ + public static function setSQLFilters( $sqlFilters, $safeMode = false ) + { + self::$flagSQLFilterSafeMode = (boolean) $safeMode; + self::$sqlFilters = $sqlFilters; + } + + /** + * Returns current SQL Filters. + * This method returns the raw SQL filter array. + * This is a lowlevel method. For a more friendly method + * please take a look at the facade: R::bindFunc(). + * + * @return array + */ + public static function getSQLFilters() + { + return self::$sqlFilters; + } + + /** + * Returns an SQL Filter snippet for reading. + * + * @param string $type type of bean + * + * @return string + */ + protected function getSQLFilterSnippet( $type ) + { + $existingCols = array(); + if (self::$flagSQLFilterSafeMode) { + $existingCols = $this->getColumns( $type ); + } + + $sqlFilters = array(); + if ( isset( self::$sqlFilters[QueryWriter::C_SQLFILTER_READ][$type] ) ) { + foreach( self::$sqlFilters[QueryWriter::C_SQLFILTER_READ][$type] as $property => $sqlFilter ) { + if ( !self::$flagSQLFilterSafeMode || isset( $existingCols[$property] ) ) { + $sqlFilters[] = $sqlFilter.' AS '.$property.' '; + } + } + } + $sqlFilterStr = ( count($sqlFilters) ) ? ( ','.implode( ',', $sqlFilters ) ) : ''; + return $sqlFilterStr; + } + /** * Generates a list of parameters (slots) for an SQL snippet. * This method calculates the correct number of slots to insert in the @@ -2968,11 +3767,7 @@ abstract class RedBean_QueryWriter_AQueryWriter { //bracket must be here - other { $sql = $this->adapter->getSQL(); - if ( strpos( $sql, '-- keep-cache' ) !== strlen( $sql ) - 13 ) { - // If SQL has been taken place outside of this method then something else then - // a select query might have happened! (or instruct to keep cache) - $this->cache = array(); - } else { + if ($this->updateCache()) { if ( isset( $this->cache[$cacheTag][$key] ) ) { return $this->cache[$cacheTag][$key]; } @@ -2981,6 +3776,27 @@ abstract class RedBean_QueryWriter_AQueryWriter { //bracket must be here - other return NULL; } + /** + * Checks if the previous query had a keep-cache tag. + * If so, the cache will persist, otherwise the cache will be flushed. + * + * Returns TRUE if the cache will remain and FALSE if a flush has + * been performed. + * + * @return boolean + */ + private function updateCache() + { + $sql = $this->adapter->getSQL(); + if ( strpos( $sql, '-- keep-cache' ) !== strlen( $sql ) - 13 ) { + // If SQL has been taken place outside of this method then something else then + // a select query might have happened! (or instruct to keep cache) + $this->cache = array(); + return FALSE; + } + return TRUE; + } + /** * Stores data from the writer in the cache under a specific key and cache tag. * A cache tag is used to make sure the cache remains consistent. In most cases the cache tag @@ -3107,6 +3923,23 @@ abstract class RedBean_QueryWriter_AQueryWriter { //bracket must be here - other return array( $sourceTable, $destTable, $linkTable, $sourceCol, $destCol ); } + /** + * Adds a data type to the list of data types. + * Use this method to add a new column type definition to the writer. + * Used for UUID support. + * + * @param integer $dataTypeID magic number constant assigned to this data type + * @param string $SQLDefinition SQL column definition (i.e. INT(11)) + * + * @return self + */ + protected function addDataType( $dataTypeID, $SQLDefinition ) + { + $this->typeno_sqltype[ $dataTypeID ] = $SQLDefinition; + $this->sqltype_typeno[ $SQLDefinition ] = $dataTypeID; + return $this; + } + /** * Returns the sql that should follow an insert statement. * @@ -3156,12 +3989,20 @@ abstract class RedBean_QueryWriter_AQueryWriter { //bracket must be here - other $table = $this->esc( $type ); if ( count( $insertvalues ) > 0 && is_array( $insertvalues[0] ) && count( $insertvalues[0] ) > 0 ) { + + $insertSlots = array(); foreach ( $insertcolumns as $k => $v ) { $insertcolumns[$k] = $this->esc( $v ); + + if (isset(self::$sqlFilters['w'][$type][$v])) { + $insertSlots[] = self::$sqlFilters['w'][$type][$v]; + } else { + $insertSlots[] = '?'; + } } $insertSQL = "INSERT INTO $table ( id, " . implode( ',', $insertcolumns ) . " ) VALUES - ( $default, " . implode( ',', array_fill( 0, count( $insertcolumns ), ' ? ' ) ) . " ) $suffix"; + ( $default, " . implode( ',', $insertSlots ) . " ) $suffix"; $ids = array(); foreach ( $insertvalues as $i => $insertvalue ) { @@ -3187,12 +4028,12 @@ abstract class RedBean_QueryWriter_AQueryWriter { //bracket must be here - other * * @return string * - * @throws RedBean_Exception_Security + * @throws Security */ protected function check( $struct ) { if ( !preg_match( '/^[a-zA-Z0-9_]+$/', $struct ) ) { - throw new RedBean_Exception_Security( 'Identifier does not conform to RedBeanPHP security policies.' ); + throw new RedException( 'Identifier does not conform to RedBeanPHP security policies.' ); } return $struct; @@ -3211,7 +4052,7 @@ abstract class RedBean_QueryWriter_AQueryWriter { //bracket must be here - other } /** - * @see RedBean_QueryWriter::getAssocTableFormat + * @see QueryWriter::getAssocTableFormat */ public static function getAssocTableFormat( $types ) { @@ -3223,7 +4064,7 @@ abstract class RedBean_QueryWriter_AQueryWriter { //bracket must be here - other } /** - * @see RedBean_QueryWriter::renameAssociation + * @see QueryWriter::renameAssociation */ public static function renameAssociation( $from, $to = NULL ) { @@ -3236,6 +4077,19 @@ abstract class RedBean_QueryWriter_AQueryWriter { //bracket must be here - other self::$renames[$from] = $to; } + /** + * Globally available service method for RedBeanPHP. + * Converts a camel cased string to a snake cased string. + * + * @param string $camel a camelCased string + * + * @return string + */ + public static function camelsSnake( $camel ) + { + return strtolower( preg_replace( '/(?<=[a-z])([A-Z])|([A-Z])(?=[a-z])/', '_$1$2', $camel ) ); + } + /** * Checks whether the specified type (i.e. table) already exists in the database. * Not part of the Object Database interface! @@ -3252,7 +4106,7 @@ abstract class RedBean_QueryWriter_AQueryWriter { //bracket must be here - other } /** - * @see RedBean_QueryWriter::glueSQLCondition + * @see QueryWriter::glueSQLCondition */ public function glueSQLCondition( $sql, $glue = NULL ) { @@ -3270,21 +4124,29 @@ abstract class RedBean_QueryWriter_AQueryWriter { //bracket must be here - other $lsql = ltrim( $sql ); - if ( preg_match( '/^(AND|OR|INNER|LEFT|RIGHT|JOIN|WHERE|ORDER|GROUP|HAVING|LIMIT|OFFSET)\s+/i', $lsql ) ) { - if ( $glue === RedBean_QueryWriter::C_GLUE_WHERE && stripos( $lsql, 'AND' ) === 0 ) { + if ( preg_match( '/^(INNER|LEFT|RIGHT|JOIN|AND|OR|WHERE|ORDER|GROUP|HAVING|LIMIT|OFFSET)\s+/i', $lsql ) ) { + if ( $glue === QueryWriter::C_GLUE_WHERE && stripos( $lsql, 'AND' ) === 0 ) { $snippetCache[$key] = ' WHERE ' . substr( $lsql, 3 ); } else { $snippetCache[$key] = $sql; } } else { - $snippetCache[$key] = ( ( $glue === RedBean_QueryWriter::C_GLUE_AND ) ? ' AND ' : ' WHERE ') . $sql; + $snippetCache[$key] = ( ( $glue === QueryWriter::C_GLUE_AND ) ? ' AND ' : ' WHERE ') . $sql; } return $snippetCache[$key]; } /** - * @see RedBean_QueryWriter::esc + * @see QueryWriter::glueLimitOne + */ + public function glueLimitOne( $sql = '') + { + return ( strpos( $sql, 'LIMIT' ) === FALSE ) ? ( $sql . ' LIMIT 1 ' ) : $sql; + } + + /** + * @see QueryWriter::esc */ public function esc( $dbStructure, $dontQuote = FALSE ) { @@ -3294,7 +4156,7 @@ abstract class RedBean_QueryWriter_AQueryWriter { //bracket must be here - other } /** - * @see RedBean_QueryWriter::addColumn + * @see QueryWriter::addColumn */ public function addColumn( $type, $column, $field ) { @@ -3309,7 +4171,7 @@ abstract class RedBean_QueryWriter_AQueryWriter { //bracket must be here - other } /** - * @see RedBean_QueryWriter::updateRecord + * @see QueryWriter::updateRecord */ public function updateRecord( $type, $updatevalues, $id = NULL ) { @@ -3337,7 +4199,13 @@ abstract class RedBean_QueryWriter_AQueryWriter { //bracket must be here - other $p = $v = array(); foreach ( $updatevalues as $uv ) { - $p[] = " {$this->esc( $uv["property"] )} = ? "; + + if ( isset( self::$sqlFilters['w'][$type][$uv['property']] ) ) { + $p[] = " {$this->esc( $uv["property"] )} = ". self::$sqlFilters['w'][$type][$uv['property']]; + } else { + $p[] = " {$this->esc( $uv["property"] )} = ? "; + } + $v[] = $uv['value']; } @@ -3351,11 +4219,25 @@ abstract class RedBean_QueryWriter_AQueryWriter { //bracket must be here - other } /** - * @see RedBean_QueryWriter::queryRecord + * @see QueryWriter::writeJoin + */ + public function writeJoin( $type, $targetType, $leftRight = 'LEFT' ) + { + if ( $leftRight !== 'LEFT' && $leftRight !== 'RIGHT' && $leftRight !== 'INNER' ) + throw new RedException( 'Invalid JOIN.' ); + + $table = $this->esc( $type ); + $targetTable = $this->esc( $targetType ); + $field = $this->esc( $targetType, TRUE ); + return " {$leftRight} JOIN {$targetTable} ON {$targetTable}.id = {$table}.{$field}_id "; + } + + /** + * @see QueryWriter::queryRecord */ public function queryRecord( $type, $conditions = array(), $addSql = NULL, $bindings = array() ) { - $addSql = $this->glueSQLCondition( $addSql, ( count($conditions) > 0) ? RedBean_QueryWriter::C_GLUE_AND : NULL ); + $addSql = $this->glueSQLCondition( $addSql, ( count($conditions) > 0) ? QueryWriter::C_GLUE_AND : NULL ); $key = NULL; if ( $this->flagUseCache ) { @@ -3368,11 +4250,19 @@ abstract class RedBean_QueryWriter_AQueryWriter { //bracket must be here - other $table = $this->esc( $type ); + $sqlFilterStr = ''; + if ( count( self::$sqlFilters ) ) { + $sqlFilterStr = $this->getSQLFilterSnippet( $type ); + } + $sql = $this->makeSQLFromConditions( $conditions, $bindings, $addSql ); - $sql = "SELECT * FROM {$table} {$sql} -- keep-cache"; + + $fieldSelection = ( self::$flagNarrowFieldMode ) ? "{$table}.*" : '*'; + $sql = "SELECT {$fieldSelection} {$sqlFilterStr} FROM {$table} {$sql} -- keep-cache"; $rows = $this->adapter->get( $sql, $bindings ); + if ( $this->flagUseCache && $key ) { $this->putResultInCache( $type, $key, $rows ); } @@ -3381,11 +4271,11 @@ abstract class RedBean_QueryWriter_AQueryWriter { //bracket must be here - other } /** - * @see RedBean_QueryWriter::queryRecordRelated + * @see QueryWriter::queryRecordRelated */ public function queryRecordRelated( $sourceType, $destType, $linkIDs, $addSql = '', $bindings = array() ) { - $addSql = $this->glueSQLCondition( $addSql, RedBean_QueryWriter::C_GLUE_WHERE ); + $addSql = $this->glueSQLCondition( $addSql, QueryWriter::C_GLUE_WHERE ); list( $sourceTable, $destTable, $linkTable, $sourceCol, $destCol ) = $this->getRelationalTablesAndColumns( $sourceType, $destType ); @@ -3397,11 +4287,16 @@ abstract class RedBean_QueryWriter_AQueryWriter { //bracket must be here - other $inClause = $this->getParametersForInClause( $linkIDs, $bindings ); + $sqlFilterStr = ''; + if ( count( self::$sqlFilters ) ) { + $sqlFilterStr = $this->getSQLFilterSnippet( $destType ); + } + if ( $sourceType === $destType ) { $inClause2 = $this->getParametersForInClause( $linkIDs, $bindings, count( $bindings ) ); //for some databases $sql = " SELECT - {$destTable}.*, + {$destTable}.* {$sqlFilterStr} , COALESCE( NULLIF({$linkTable}.{$sourceCol}, {$destTable}.id), NULLIF({$linkTable}.{$destCol}, {$destTable}.id)) AS linked_by @@ -3416,7 +4311,7 @@ abstract class RedBean_QueryWriter_AQueryWriter { //bracket must be here - other } else { $sql = " SELECT - {$destTable}.*, + {$destTable}.* {$sqlFilterStr}, {$linkTable}.{$sourceCol} AS linked_by FROM {$linkTable} INNER JOIN {$destTable} ON @@ -3435,55 +4330,7 @@ abstract class RedBean_QueryWriter_AQueryWriter { //bracket must be here - other } /** - * @see RedBean_QueryWriter::queryRecordLinks - */ - public function queryRecordLinks( $sourceType, $destType, $linkIDs, $addSql = '', $bindings = array() ) - { - $addSql = $this->glueSQLCondition( $addSql, RedBean_QueryWriter::C_GLUE_WHERE ); - - list( $sourceTable, $destTable, $linkTable, $sourceCol, $destCol ) = $this->getRelationalTablesAndColumns( $sourceType, $destType ); - - $key = $this->getCacheKey( array( $sourceType, $destType, implode( ',', $linkIDs ), $addSql, $bindings ) ); - - if ( $this->flagUseCache && $cached = $this->getCached( $linkTable, $key ) ) { - return $cached; - } - - $inClause = $this->getParametersForInClause( $linkIDs, $bindings ); - - $selector = "{$linkTable}.*"; - - if ( $sourceType === $destType ) { - $inClause2 = $this->getParametersForInClause( $linkIDs, $bindings, count( $bindings ) ); //for some databases - $sql = " - SELECT {$selector} FROM {$linkTable} - INNER JOIN {$destTable} ON - ( {$destTable}.id = {$linkTable}.{$destCol} AND {$linkTable}.{$sourceCol} IN ($inClause) ) OR - ( {$destTable}.id = {$linkTable}.{$sourceCol} AND {$linkTable}.{$destCol} IN ($inClause2) ) - {$addSql} - -- keep-cache"; - - $linkIDs = array_merge( $linkIDs, $linkIDs ); - } else { - $sql = " - SELECT {$selector} FROM {$linkTable} - INNER JOIN {$destTable} ON - ( {$destTable}.id = {$linkTable}.{$destCol} AND {$linkTable}.{$sourceCol} IN ($inClause) ) - {$addSql} - -- keep-cache"; - } - - $bindings = array_merge( $linkIDs, $bindings ); - - $rows = $this->adapter->get( $sql, $bindings ); - - $this->putResultInCache( $linkTable, $key, $rows ); - - return $rows; - } - - /** - * @see RedBean_QueryWriter::queryRecordLink + * @see QueryWriter::queryRecordLink */ public function queryRecordLink( $sourceType, $destType, $sourceID, $destID ) { @@ -3495,13 +4342,18 @@ abstract class RedBean_QueryWriter_AQueryWriter { //bracket must be here - other return $cached; } + $sqlFilterStr = ''; + if ( count( self::$sqlFilters ) ) { + $sqlFilterStr = $this->getSQLFilterSnippet( $destType ); + } + if ( $sourceTable === $destTable ) { - $sql = "SELECT {$linkTable}.* FROM {$linkTable} + $sql = "SELECT {$linkTable}.* {$sqlFilterStr} FROM {$linkTable} WHERE ( {$sourceCol} = ? AND {$destCol} = ? ) OR ( {$destCol} = ? AND {$sourceCol} = ? ) -- keep-cache"; $row = $this->adapter->getRow( $sql, array( $sourceID, $destID, $sourceID, $destID ) ); } else { - $sql = "SELECT {$linkTable}.* FROM {$linkTable} + $sql = "SELECT {$linkTable}.* {$sqlFilterStr} FROM {$linkTable} WHERE {$sourceCol} = ? AND {$destCol} = ? -- keep-cache"; $row = $this->adapter->getRow( $sql, array( $sourceID, $destID ) ); } @@ -3512,7 +4364,34 @@ abstract class RedBean_QueryWriter_AQueryWriter { //bracket must be here - other } /** - * @see RedBean_QueryWriter::queryRecordCount + * @see QueryWriter::queryTagged + */ + public function queryTagged( $type, $tagList, $all = FALSE, $addSql = '', $bindings = array() ) + { + $assocType = $this->getAssocTable( array( $type, 'tag' ) ); + $assocTable = $this->esc( $assocType ); + $assocField = $type . '_id'; + $table = $this->esc( $type ); + $slots = implode( ',', array_fill( 0, count( $tagList ), '?' ) ); + $score = ( $all ) ? count( $tagList ) : 1; + + $sql = " + SELECT {$table}.*, count({$table}.id) FROM {$table} + INNER JOIN {$assocTable} ON {$assocField} = {$table}.id + INNER JOIN tag ON {$assocTable}.tag_id = tag.id + WHERE tag.title IN ({$slots}) + GROUP BY {$table}.id + HAVING count({$table}.id) >= ? + {$addSql} + "; + + $bindings = array_merge( $tagList, array( $score ), $bindings ); + $rows = $this->adapter->get( $sql, $bindings ); + return $rows; + } + + /** + * @see QueryWriter::queryRecordCount */ public function queryRecordCount( $type, $conditions = array(), $addSql = NULL, $bindings = array() ) { @@ -3520,26 +4399,31 @@ abstract class RedBean_QueryWriter_AQueryWriter { //bracket must be here - other $table = $this->esc( $type ); + $this->updateCache(); //check if cache chain has been broken + $sql = $this->makeSQLFromConditions( $conditions, $bindings, $addSql ); - $sql = "SELECT COUNT(*) FROM {$table} {$sql}"; + $sql = "SELECT COUNT(*) FROM {$table} {$sql} -- keep-cache"; return (int) $this->adapter->getCell( $sql, $bindings ); } /** - * @see RedBean_QueryWriter::queryRecordCountRelated + * @see QueryWriter::queryRecordCountRelated */ public function queryRecordCountRelated( $sourceType, $destType, $linkID, $addSql = '', $bindings = array() ) { list( $sourceTable, $destTable, $linkTable, $sourceCol, $destCol ) = $this->getRelationalTablesAndColumns( $sourceType, $destType ); + $this->updateCache(); //check if cache chain has been broken + if ( $sourceType === $destType ) { $sql = " SELECT COUNT(*) FROM {$linkTable} INNER JOIN {$destTable} ON ( {$destTable}.id = {$linkTable}.{$destCol} AND {$linkTable}.{$sourceCol} = ? ) OR ( {$destTable}.id = {$linkTable}.{$sourceCol} AND {$linkTable}.{$destCol} = ? ) - {$addSql}"; + {$addSql} + -- keep-cache"; $bindings = array_merge( array( $linkID, $linkID ), $bindings ); } else { @@ -3547,7 +4431,8 @@ abstract class RedBean_QueryWriter_AQueryWriter { //bracket must be here - other SELECT COUNT(*) FROM {$linkTable} INNER JOIN {$destTable} ON ( {$destTable}.id = {$linkTable}.{$destCol} AND {$linkTable}.{$sourceCol} = ? ) - {$addSql}"; + {$addSql} + -- keep-cache"; $bindings = array_merge( array( $linkID ), $bindings ); } @@ -3556,7 +4441,7 @@ abstract class RedBean_QueryWriter_AQueryWriter { //bracket must be here - other } /** - * @see RedBean_QueryWriter::deleteRecord + * @see QueryWriter::deleteRecord */ public function deleteRecord( $type, $conditions = array(), $addSql = NULL, $bindings = array() ) { @@ -3571,7 +4456,7 @@ abstract class RedBean_QueryWriter_AQueryWriter { //bracket must be here - other } /** - * @see RedBean_QueryWriter::deleteRelations + * @see QueryWriter::deleteRelations */ public function deleteRelations( $sourceType, $destType, $sourceID ) { @@ -3593,7 +4478,7 @@ abstract class RedBean_QueryWriter_AQueryWriter { //bracket must be here - other } /** - * @see RedBean_QueryWriter::widenColumn + * @see QueryWriter::widenColumn */ public function widenColumn( $type, $column, $datatype ) { @@ -3611,7 +4496,7 @@ abstract class RedBean_QueryWriter_AQueryWriter { //bracket must be here - other } /** - * @see RedBean_QueryWriter::wipe + * @see QueryWriter::wipe */ public function wipe( $type ) { @@ -3621,59 +4506,7 @@ abstract class RedBean_QueryWriter_AQueryWriter { //bracket must be here - other } /** - * @see RedBean_QueryWriter::addFK - */ - public function addFK( $type, $targetType, $field, $targetField, $isDependent = FALSE ) - { - $table = $this->esc( $type ); - $tableNoQ = $this->esc( $type, TRUE ); - - $targetTable = $this->esc( $targetType ); - - $column = $this->esc( $field ); - $columnNoQ = $this->esc( $field, TRUE ); - - $targetColumn = $this->esc( $targetField ); - $targetColumnNoQ = $this->esc( $targetField, TRUE ); - - $db = $this->adapter->getCell( 'SELECT DATABASE()' ); - - $fkName = 'fk_' . $tableNoQ . '_' . $columnNoQ . '_' . $targetColumnNoQ . ( $isDependent ? '_casc' : '' ); - $cName = 'cons_' . $fkName; - - $cfks = $this->adapter->getCell( " - SELECT CONSTRAINT_NAME - FROM information_schema.KEY_COLUMN_USAGE - WHERE TABLE_SCHEMA ='$db' AND TABLE_NAME = '$tableNoQ' AND COLUMN_NAME = '$columnNoQ' AND - CONSTRAINT_NAME <>'PRIMARY' AND REFERENCED_TABLE_NAME is not null - " ); - - $flagAddKey = FALSE; - - try { - // No keys - if ( !$cfks ) { - $flagAddKey = TRUE; //go get a new key - } - - // Has fk, but different setting, --remove - if ( $cfks && $cfks != $cName ) { - $this->adapter->exec( "ALTER TABLE $table DROP FOREIGN KEY $cfks " ); - $flagAddKey = TRUE; //go get a new key. - } - - if ( $flagAddKey ) { - $this->adapter->exec( "ALTER TABLE $table - ADD CONSTRAINT $cName FOREIGN KEY $fkName ( $column ) REFERENCES $targetTable ( - $targetColumn) ON DELETE " . ( $isDependent ? 'CASCADE' : 'SET NULL' ) . ' ON UPDATE SET NULL ;' ); - } - } catch ( Exception $e ) { - // Failure of fk-constraints is not a problem - } - } - - /** - * @see RedBean_QueryWriter::renameAssocTable + * @see QueryWriter::renameAssocTable */ public function renameAssocTable( $from, $to = NULL ) { @@ -3681,7 +4514,7 @@ abstract class RedBean_QueryWriter_AQueryWriter { //bracket must be here - other } /** - * @see RedBean_QueryWriter::getAssocTable + * @see QueryWriter::getAssocTable */ public function getAssocTable( $types ) { @@ -3689,13 +4522,13 @@ abstract class RedBean_QueryWriter_AQueryWriter { //bracket must be here - other } /** - * @see RedBean_QueryWriter::addConstraintForTypes + * @see QueryWriter::addConstraintForTypes */ public function addConstraintForTypes( $sourceType, $destType ) { list( $sourceTable, $destTable, $linkTable, $sourceCol, $destCol ) = $this->getRelationalTablesAndColumns( $sourceType, $destType, TRUE ); - $this->constrain( $linkTable, $sourceTable, $destTable, $sourceCol, $destCol ); + return $this->constrain( $linkTable, $sourceTable, $destTable, $sourceCol, $destCol ); } /** @@ -3747,30 +4580,35 @@ abstract class RedBean_QueryWriter_AQueryWriter { //bracket must be here - other { return $this->esc( $table, $noQuotes ); } - - /** - * @deprecated Use addContraintForTypes instead. - * - * @param RedBean_OODBBean $bean1 bean - * @param RedBean_OODBBean $bean2 bean - * - * @return void - */ - public function addConstraint( RedBean_OODBBean $bean1, RedBean_OODBBean $bean2 ) - { - $this->addConstraintForTypes( $bean1->getMeta( 'type' ), $bean2->getMeta( 'type' ) ); - } +} } +namespace RedBeanPHP\QueryWriter { -class RedBean_QueryWriter_MySQL extends RedBean_QueryWriter_AQueryWriter implements RedBean_QueryWriter +use RedBeanPHP\QueryWriter\AQueryWriter as AQueryWriter; +use RedBeanPHP\QueryWriter as QueryWriter; +use RedBeanPHP\Adapter\DBAdapter as DBAdapter; +use RedBeanPHP\Adapter as Adapter; + +/** + * RedBean MySQLWriter + * + * @file RedBean/QueryWriter/MySQL.php + * @desc Represents a MySQL Database to RedBean + * @author Gabor de Mooij and the RedBeanPHP Community + * @license BSD/GPLv2 + * + * (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community. + * This source file is subject to the BSD/GPLv2 License that is bundled + * with this source code in the file license.txt. + */ +class MySQL extends AQueryWriter implements QueryWriter { /** * Data types */ const C_DATATYPE_BOOL = 0; - const C_DATATYPE_UINT8 = 1; const C_DATATYPE_UINT32 = 2; const C_DATATYPE_DOUBLE = 3; const C_DATATYPE_TEXT8 = 4; @@ -3779,10 +4617,13 @@ class RedBean_QueryWriter_MySQL extends RedBean_QueryWriter_AQueryWriter impleme const C_DATATYPE_SPECIAL_DATE = 80; const C_DATATYPE_SPECIAL_DATETIME = 81; const C_DATATYPE_SPECIAL_POINT = 90; + const C_DATATYPE_SPECIAL_LINESTRING = 91; + const C_DATATYPE_SPECIAL_POLYGON = 92; + const C_DATATYPE_SPECIFIED = 99; /** - * @var RedBean_Adapter_DBAdapter + * @var DBAdapter */ protected $adapter; @@ -3794,8 +4635,6 @@ class RedBean_QueryWriter_MySQL extends RedBean_QueryWriter_AQueryWriter impleme /** * Add the constraints for a specific database driver: MySQL. * - * @todo Too many arguments; find a way to solve this in a neater way. - * * @param string $table table table to add constrains to * @param string $table1 table1 first reference table * @param string $table2 table2 second reference table @@ -3824,30 +4663,32 @@ class RedBean_QueryWriter_MySQL extends RedBean_QueryWriter_AQueryWriter impleme $columns = $this->getColumns( $table ); - if ( $this->code( $columns[$property1] ) !== RedBean_QueryWriter_MySQL::C_DATATYPE_UINT32 ) { - $this->widenColumn( $table, $property1, RedBean_QueryWriter_MySQL::C_DATATYPE_UINT32 ); + $idType = $this->getTypeForID(); + + if ( $this->code( $columns[$property1] ) !== $idType ) { + $this->widenColumn( $table, $property1, $idType ); } - if ( $this->code( $columns[$property2] ) !== RedBean_QueryWriter_MySQL::C_DATATYPE_UINT32 ) { - $this->widenColumn( $table, $property2, RedBean_QueryWriter_MySQL::C_DATATYPE_UINT32 ); + if ( $this->code( $columns[$property2] ) !== $idType ) { + $this->widenColumn( $table, $property2, $idType ); } $sql = " ALTER TABLE " . $this->esc( $table ) . " - ADD FOREIGN KEY($property1) references `$table1`(id) ON DELETE CASCADE; + ADD FOREIGN KEY($property1) references `$table1`(id) ON DELETE CASCADE ON UPDATE CASCADE; "; $this->adapter->exec( $sql ); $sql = " ALTER TABLE " . $this->esc( $table ) . " - ADD FOREIGN KEY($property2) references `$table2`(id) ON DELETE CASCADE + ADD FOREIGN KEY($property2) references `$table2`(id) ON DELETE CASCADE ON UPDATE CASCADE "; $this->adapter->exec( $sql ); return TRUE; - } catch ( Exception $e ) { + } catch ( \Exception $e ) { return FALSE; } } @@ -3855,21 +4696,22 @@ class RedBean_QueryWriter_MySQL extends RedBean_QueryWriter_AQueryWriter impleme /** * Constructor * - * @param RedBean_Adapter $adapter Database Adapter + * @param Adapter $adapter Database Adapter */ - public function __construct( RedBean_Adapter $adapter ) + public function __construct( Adapter $adapter ) { $this->typeno_sqltype = array( - RedBean_QueryWriter_MySQL::C_DATATYPE_BOOL => ' TINYINT(1) UNSIGNED ', - RedBean_QueryWriter_MySQL::C_DATATYPE_UINT8 => ' TINYINT(3) UNSIGNED ', - RedBean_QueryWriter_MySQL::C_DATATYPE_UINT32 => ' INT(11) UNSIGNED ', - RedBean_QueryWriter_MySQL::C_DATATYPE_DOUBLE => ' DOUBLE ', - RedBean_QueryWriter_MySQL::C_DATATYPE_TEXT8 => ' VARCHAR(255) ', - RedBean_QueryWriter_MySQL::C_DATATYPE_TEXT16 => ' TEXT ', - RedBean_QueryWriter_MySQL::C_DATATYPE_TEXT32 => ' LONGTEXT ', - RedBean_QueryWriter_MySQL::C_DATATYPE_SPECIAL_DATE => ' DATE ', - RedBean_QueryWriter_MySQL::C_DATATYPE_SPECIAL_DATETIME => ' DATETIME ', - RedBean_QueryWriter_MySQL::C_DATATYPE_SPECIAL_POINT => ' POINT ', + MySQL::C_DATATYPE_BOOL => ' TINYINT(1) UNSIGNED ', + MySQL::C_DATATYPE_UINT32 => ' INT(11) UNSIGNED ', + MySQL::C_DATATYPE_DOUBLE => ' DOUBLE ', + MySQL::C_DATATYPE_TEXT8 => ' VARCHAR(255) ', + MySQL::C_DATATYPE_TEXT16 => ' TEXT ', + MySQL::C_DATATYPE_TEXT32 => ' LONGTEXT ', + MySQL::C_DATATYPE_SPECIAL_DATE => ' DATE ', + MySQL::C_DATATYPE_SPECIAL_DATETIME => ' DATETIME ', + MySQL::C_DATATYPE_SPECIAL_POINT => ' POINT ', + MySQL::C_DATATYPE_SPECIAL_LINESTRING => ' LINESTRING ', + MySQL::C_DATATYPE_SPECIAL_POLYGON => ' POLYGON ', ); $this->sqltype_typeno = array(); @@ -3895,7 +4737,7 @@ class RedBean_QueryWriter_MySQL extends RedBean_QueryWriter_AQueryWriter impleme } /** - * @see RedBean_QueryWriter::getTables + * @see QueryWriter::getTables */ public function getTables() { @@ -3903,7 +4745,7 @@ class RedBean_QueryWriter_MySQL extends RedBean_QueryWriter_AQueryWriter impleme } /** - * @see RedBean_QueryWriter::createTable + * @see QueryWriter::createTable */ public function createTable( $table ) { @@ -3916,7 +4758,7 @@ class RedBean_QueryWriter_MySQL extends RedBean_QueryWriter_AQueryWriter impleme } /** - * @see RedBean_QueryWriter::getColumns + * @see QueryWriter::getColumns */ public function getColumns( $table ) { @@ -3931,56 +4773,61 @@ class RedBean_QueryWriter_MySQL extends RedBean_QueryWriter_AQueryWriter impleme } /** - * @see RedBean_QueryWriter::scanType + * @see QueryWriter::scanType */ public function scanType( $value, $flagSpecial = FALSE ) { $this->svalue = $value; - if ( is_null( $value ) ) return RedBean_QueryWriter_MySQL::C_DATATYPE_BOOL; + if ( is_null( $value ) ) return MySQL::C_DATATYPE_BOOL; if ( $flagSpecial ) { if ( preg_match( '/^\d{4}\-\d\d-\d\d$/', $value ) ) { - return RedBean_QueryWriter_MySQL::C_DATATYPE_SPECIAL_DATE; + return MySQL::C_DATATYPE_SPECIAL_DATE; } if ( preg_match( '/^\d{4}\-\d\d-\d\d\s\d\d:\d\d:\d\d$/', $value ) ) { - return RedBean_QueryWriter_MySQL::C_DATATYPE_SPECIAL_DATETIME; + return MySQL::C_DATATYPE_SPECIAL_DATETIME; + } + if ( preg_match( '/^POINT\(/', $value ) ) { + return MySQL::C_DATATYPE_SPECIAL_POINT; + } + if ( preg_match( '/^LINESTRING\(/', $value ) ) { + return MySQL::C_DATATYPE_SPECIAL_LINESTRING; + } + if ( preg_match( '/^POLYGON\(/', $value ) ) { + return MySQL::C_DATATYPE_SPECIAL_POLYGON; } } - $value = strval( $value ); + //setter turns TRUE FALSE into 0 and 1 because database has no real bools (TRUE and FALSE only for test?). + if ( $value === FALSE || $value === TRUE || $value === '0' || $value === '1' ) { + return MySQL::C_DATATYPE_BOOL; + } if ( !$this->startsWithZeros( $value ) ) { - if ( $value === TRUE || $value === FALSE || $value === '1' || $value === '' ) { - return RedBean_QueryWriter_MySQL::C_DATATYPE_BOOL; - } - - if ( is_numeric( $value ) && ( floor( $value ) == $value ) && $value >= 0 && $value <= 255 ) { - return RedBean_QueryWriter_MySQL::C_DATATYPE_UINT8; - } if ( is_numeric( $value ) && ( floor( $value ) == $value ) && $value >= 0 && $value <= 4294967295 ) { - return RedBean_QueryWriter_MySQL::C_DATATYPE_UINT32; + return MySQL::C_DATATYPE_UINT32; } if ( is_numeric( $value ) ) { - return RedBean_QueryWriter_MySQL::C_DATATYPE_DOUBLE; + return MySQL::C_DATATYPE_DOUBLE; } } if ( mb_strlen( $value, 'UTF-8' ) <= 255 ) { - return RedBean_QueryWriter_MySQL::C_DATATYPE_TEXT8; + return MySQL::C_DATATYPE_TEXT8; } if ( mb_strlen( $value, 'UTF-8' ) <= 65535 ) { - return RedBean_QueryWriter_MySQL::C_DATATYPE_TEXT16; + return MySQL::C_DATATYPE_TEXT16; } - return RedBean_QueryWriter_MySQL::C_DATATYPE_TEXT32; + return MySQL::C_DATATYPE_TEXT32; } /** - * @see RedBean_QueryWriter::code + * @see QueryWriter::code */ public function code( $typedescription, $includeSpecials = FALSE ) { @@ -3994,7 +4841,7 @@ class RedBean_QueryWriter_MySQL extends RedBean_QueryWriter_AQueryWriter impleme return $r; } - if ( $r >= RedBean_QueryWriter::C_DATATYPE_RANGE_SPECIAL ) { + if ( $r >= QueryWriter::C_DATATYPE_RANGE_SPECIAL ) { return self::C_DATATYPE_SPECIFIED; } @@ -4002,7 +4849,7 @@ class RedBean_QueryWriter_MySQL extends RedBean_QueryWriter_AQueryWriter impleme } /** - * @see RedBean_QueryWriter::addUniqueIndex + * @see QueryWriter::addUniqueIndex */ public function addUniqueIndex( $table, $columns ) { @@ -4026,14 +4873,18 @@ class RedBean_QueryWriter_MySQL extends RedBean_QueryWriter_AQueryWriter impleme } } - $sql = "ALTER IGNORE TABLE $table - ADD UNIQUE INDEX $name (" . implode( ',', $columns ) . ")"; + try { + $sql = "ALTER TABLE $table + ADD UNIQUE INDEX $name (" . implode( ',', $columns ) . ")"; + } catch ( \Exception $e ) { + //do nothing, dont use alter table ignore, this will delete duplicate records in 3-ways! + } $this->adapter->exec( $sql ); } /** - * @see RedBean_QueryWriter::addIndex + * @see QueryWriter::addIndex */ public function addIndex( $type, $name, $column ) { @@ -4044,30 +4895,64 @@ class RedBean_QueryWriter_MySQL extends RedBean_QueryWriter_AQueryWriter impleme $column = $this->esc( $column ); - foreach ( $this->adapter->get( "SHOW INDEX FROM $table " ) as $ind ) if ( $ind['Key_name'] === $name ) return; - try { + foreach ( $this->adapter->get( "SHOW INDEX FROM $table " ) as $ind ) if ( $ind['Key_name'] === $name ) return; $this->adapter->exec( "CREATE INDEX $name ON $table ($column) " ); - } catch ( Exception $e ) { + } catch (\Exception $e ) { } } /** - * @see RedBean_QueryWriter::sqlStateIn + * @see QueryWriter::addFK + */ + public function addFK( $type, $targetType, $field, $targetField, $isDependent = FALSE ) + { + + $db = $this->adapter->getCell( 'SELECT DATABASE()' ); + + $cfks = $this->adapter->getCell(' + SELECT CONSTRAINT_NAME + FROM information_schema.KEY_COLUMN_USAGE + WHERE + TABLE_SCHEMA = ? + AND TABLE_NAME = ? + AND COLUMN_NAME = ? AND + CONSTRAINT_NAME != \'PRIMARY\' + AND REFERENCED_TABLE_NAME IS NOT NULL + ', array($db, $type, $field)); + + if ($cfks) return; + + try { + $fkName = 'fk_'.($type.'_'.$field); + $cName = 'c_'.$fkName; + $this->adapter->exec( " + ALTER TABLE {$this->esc($type)} + ADD CONSTRAINT $cName + FOREIGN KEY $fkName ( {$this->esc($field)} ) REFERENCES {$this->esc($targetType)} ( + {$this->esc($targetField)}) ON DELETE " . ( $isDependent ? 'CASCADE' : 'SET NULL' ) . ' ON UPDATE '.( $isDependent ? 'CASCADE' : 'SET NULL' ).';'); + + } catch (\Exception $e ) { + // Failure of fk-constraints is not a problem + } + } + + /** + * @see QueryWriter::sqlStateIn */ public function sqlStateIn( $state, $list ) { $stateMap = array( - '42S02' => RedBean_QueryWriter::C_SQLSTATE_NO_SUCH_TABLE, - '42S22' => RedBean_QueryWriter::C_SQLSTATE_NO_SUCH_COLUMN, - '23000' => RedBean_QueryWriter::C_SQLSTATE_INTEGRITY_CONSTRAINT_VIOLATION + '42S02' => QueryWriter::C_SQLSTATE_NO_SUCH_TABLE, + '42S22' => QueryWriter::C_SQLSTATE_NO_SUCH_COLUMN, + '23000' => QueryWriter::C_SQLSTATE_INTEGRITY_CONSTRAINT_VIOLATION ); return in_array( ( isset( $stateMap[$state] ) ? $stateMap[$state] : '0' ), $list ); } /** - * @see RedBean_QueryWriter::wipeAll + * @see QueryWriter::wipeAll */ public function wipeAll() { @@ -4076,24 +4961,43 @@ class RedBean_QueryWriter_MySQL extends RedBean_QueryWriter_AQueryWriter impleme foreach ( $this->getTables() as $t ) { try { $this->adapter->exec( "DROP TABLE IF EXISTS `$t`" ); - } catch ( Exception $e ) { + } catch (\Exception $e ) { } try { $this->adapter->exec( "DROP VIEW IF EXISTS `$t`" ); - } catch ( Exception $e ) { + } catch (\Exception $e ) { } } $this->adapter->exec( 'SET FOREIGN_KEY_CHECKS = 1;' ); } } +} +namespace RedBeanPHP\QueryWriter { -class RedBean_QueryWriter_SQLiteT extends RedBean_QueryWriter_AQueryWriter implements RedBean_QueryWriter +use RedBeanPHP\QueryWriter\AQueryWriter as AQueryWriter; +use RedBeanPHP\QueryWriter as QueryWriter; +use RedBeanPHP\Adapter\DBAdapter as DBAdapter; +use RedBeanPHP\Adapter as Adapter; + +/** + * RedBean SQLiteWriter with support for SQLite types + * + * @file RedBean/QueryWriter/SQLiteT.php + * @desc Represents a SQLite Database to RedBean + * @author Gabor de Mooij and the RedBeanPHP Community + * @license BSD/GPLv2 + * + * (c) copyright G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community. + * This source file is subject to the BSD/GPLv2 License that is bundled + * with this source code in the file license.txt. + */ +class SQLiteT extends AQueryWriter implements QueryWriter { /** - * @var RedBean_Adapter_DBAdapter + * @var DBAdapter */ protected $adapter; @@ -4260,21 +5164,17 @@ class RedBean_QueryWriter_SQLiteT extends RedBean_QueryWriter_AQueryWriter imple $consSQL = ( $constraint ? 'CASCADE' : 'SET NULL' ); $t = $this->getTable( $type ); - $label = 'from_' . $field . '_to_table_' . $targetType . '_col_' . $targetField; - if ( isset( $t['keys'][$label] ) - && $t['keys'][$label]['table'] === $targetType - && $t['keys'][$label]['from'] === $field - && $t['keys'][$label]['to'] === $targetField - && $t['keys'][$label]['on_delete'] === $consSQL - ) return FALSE; + foreach($t['keys'] as $key) { + if ($key['from'] === $field) return FALSE; + } $t['keys'][$label] = array( 'table' => $targetType, 'from' => $field, 'to' => $targetField, - 'on_update' => 'SET NULL', + 'on_update' => $consSQL, 'on_delete' => $consSQL ); @@ -4305,14 +5205,14 @@ class RedBean_QueryWriter_SQLiteT extends RedBean_QueryWriter_AQueryWriter imple /** * Constructor * - * @param RedBean_Adapter $adapter Database Adapter + * @param Adapter $adapter Database Adapter */ - public function __construct( RedBean_Adapter $adapter ) + public function __construct( Adapter $adapter ) { $this->typeno_sqltype = array( - RedBean_QueryWriter_SQLiteT::C_DATATYPE_INTEGER => 'INTEGER', - RedBean_QueryWriter_SQLiteT::C_DATATYPE_NUMERIC => 'NUMERIC', - RedBean_QueryWriter_SQLiteT::C_DATATYPE_TEXT => 'TEXT', + SQLiteT::C_DATATYPE_INTEGER => 'INTEGER', + SQLiteT::C_DATATYPE_NUMERIC => 'NUMERIC', + SQLiteT::C_DATATYPE_TEXT => 'TEXT', ); $this->sqltype_typeno = array(); @@ -4336,19 +5236,19 @@ class RedBean_QueryWriter_SQLiteT extends RedBean_QueryWriter_AQueryWriter imple } /** - * @see RedBean_QueryWriter::scanType + * @see QueryWriter::scanType */ public function scanType( $value, $flagSpecial = FALSE ) { $this->svalue = $value; - if ( $value === FALSE ) return self::C_DATATYPE_INTEGER; - if ( $value === NULL ) return self::C_DATATYPE_INTEGER; if ( $this->startsWithZeros( $value ) ) return self::C_DATATYPE_TEXT; - if ( is_numeric( $value ) && ( intval( $value ) == $value ) && $value < 2147483648 ) return self::C_DATATYPE_INTEGER; + if ( $value === TRUE || $value === FALSE ) return self::C_DATATYPE_INTEGER; + + if ( is_numeric( $value ) && ( intval( $value ) == $value ) && $value < 2147483648 && $value > -2147483648 ) return self::C_DATATYPE_INTEGER; if ( ( is_numeric( $value ) && $value < 2147483648 ) || preg_match( '/\d{4}\-\d\d\-\d\d/', $value ) @@ -4361,7 +5261,7 @@ class RedBean_QueryWriter_SQLiteT extends RedBean_QueryWriter_AQueryWriter imple } /** - * @see RedBean_QueryWriter::addColumn + * @see QueryWriter::addColumn */ public function addColumn( $table, $column, $type ) { @@ -4373,7 +5273,7 @@ class RedBean_QueryWriter_SQLiteT extends RedBean_QueryWriter_AQueryWriter imple } /** - * @see RedBean_QueryWriter::code + * @see QueryWriter::code */ public function code( $typedescription, $includeSpecials = FALSE ) { @@ -4383,7 +5283,7 @@ class RedBean_QueryWriter_SQLiteT extends RedBean_QueryWriter_AQueryWriter imple } /** - * @see RedBean_QueryWriter::widenColumn + * @see QueryWriter::widenColumn */ public function widenColumn( $type, $column, $datatype ) { @@ -4395,7 +5295,7 @@ class RedBean_QueryWriter_SQLiteT extends RedBean_QueryWriter_AQueryWriter imple } /** - * @see RedBean_QueryWriter::getTables(); + * @see QueryWriter::getTables(); */ public function getTables() { @@ -4404,7 +5304,7 @@ class RedBean_QueryWriter_SQLiteT extends RedBean_QueryWriter_AQueryWriter imple } /** - * @see RedBean_QueryWriter::createTable + * @see QueryWriter::createTable */ public function createTable( $table ) { @@ -4416,7 +5316,7 @@ class RedBean_QueryWriter_SQLiteT extends RedBean_QueryWriter_AQueryWriter imple } /** - * @see RedBean_QueryWriter::getColumns + * @see QueryWriter::getColumns */ public function getColumns( $table ) { @@ -4431,7 +5331,7 @@ class RedBean_QueryWriter_SQLiteT extends RedBean_QueryWriter_AQueryWriter imple } /** - * @see RedBean_QueryWriter::addUniqueIndex + * @see QueryWriter::addUniqueIndex */ public function addUniqueIndex( $type, $columns ) { @@ -4447,20 +5347,20 @@ class RedBean_QueryWriter_SQLiteT extends RedBean_QueryWriter_AQueryWriter imple } /** - * @see RedBean_QueryWriter::sqlStateIn + * @see QueryWriter::sqlStateIn */ public function sqlStateIn( $state, $list ) { $stateMap = array( - 'HY000' => RedBean_QueryWriter::C_SQLSTATE_NO_SUCH_TABLE, - '23000' => RedBean_QueryWriter::C_SQLSTATE_INTEGRITY_CONSTRAINT_VIOLATION + 'HY000' => QueryWriter::C_SQLSTATE_NO_SUCH_TABLE, + '23000' => QueryWriter::C_SQLSTATE_INTEGRITY_CONSTRAINT_VIOLATION ); return in_array( ( isset( $stateMap[$state] ) ? $stateMap[$state] : '0' ), $list ); } /** - * @see RedBean_QueryWriter::addIndex + * @see QueryWriter::addIndex */ public function addIndex( $type, $name, $column ) { @@ -4470,18 +5370,23 @@ class RedBean_QueryWriter_SQLiteT extends RedBean_QueryWriter_AQueryWriter imple $name = preg_replace( '/\W/', '', $name ); $column = $this->esc( $column, TRUE ); - foreach ( $this->adapter->get( "PRAGMA INDEX_LIST($table) " ) as $ind ) { - if ( $ind['name'] === $name ) return; + try { + + foreach ( $this->adapter->get( "PRAGMA INDEX_LIST($table) " ) as $ind ) { + if ( $ind['name'] === $name ) return; + } + + $t = $this->getTable( $type ); + $t['indexes'][$name] = array( 'name' => $column ); + + $this->putTable( $t ); + } catch( \Exception $exception ) { + //do nothing } - - $t = $this->getTable( $type ); - $t['indexes'][$name] = array( 'name' => $column ); - - $this->putTable( $t ); } /** - * @see RedBean_QueryWriter::wipe + * @see QueryWriter::wipe */ public function wipe( $type ) { @@ -4491,7 +5396,7 @@ class RedBean_QueryWriter_SQLiteT extends RedBean_QueryWriter_AQueryWriter imple } /** - * @see RedBean_QueryWriter::addFK + * @see QueryWriter::addFK */ public function addFK( $type, $targetType, $field, $targetField, $isDep = FALSE ) { @@ -4499,7 +5404,7 @@ class RedBean_QueryWriter_SQLiteT extends RedBean_QueryWriter_AQueryWriter imple } /** - * @see RedBean_QueryWriter::wipeAll + * @see QueryWriter::wipeAll */ public function wipeAll() { @@ -4508,21 +5413,40 @@ class RedBean_QueryWriter_SQLiteT extends RedBean_QueryWriter_AQueryWriter imple foreach ( $this->getTables() as $t ) { try { $this->adapter->exec( "DROP TABLE IF EXISTS `$t`" ); - } catch ( Exception $e ) { + } catch (\Exception $e ) { } try { $this->adapter->exec( "DROP TABLE IF EXISTS `$t`" ); - } catch ( Exception $e ) { + } catch (\Exception $e ) { } } $this->adapter->exec( 'PRAGMA foreign_keys = 1 ' ); } } +} +namespace RedBeanPHP\QueryWriter { -class RedBean_QueryWriter_PostgreSQL extends RedBean_QueryWriter_AQueryWriter implements RedBean_QueryWriter +use RedBeanPHP\QueryWriter\AQueryWriter as AQueryWriter; +use RedBeanPHP\QueryWriter as QueryWriter; +use RedBeanPHP\Adapter\DBAdapter as DBAdapter; +use RedBeanPHP\Adapter as Adapter; + +/** + * RedBean PostgreSQL Query Writer + * + * @file RedBean/QueryWriter/PostgreSQL.php + * @desc QueryWriter for the PostgreSQL database system. + * @author Gabor de Mooij and the RedBeanPHP Community + * @license BSD/GPLv2 + * + * (c) copyright G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community. + * This source file is subject to the BSD/GPLv2 License that is bundled + * with this source code in the file license.txt. + */ +class PostgreSQL extends AQueryWriter implements QueryWriter { /** * Data types @@ -4536,10 +5460,11 @@ class RedBean_QueryWriter_PostgreSQL extends RedBean_QueryWriter_AQueryWriter im const C_DATATYPE_SPECIAL_LSEG = 91; const C_DATATYPE_SPECIAL_CIRCLE = 92; const C_DATATYPE_SPECIAL_MONEY = 93; + const C_DATATYPE_SPECIAL_POLYGON = 94; const C_DATATYPE_SPECIFIED = 99; /** - * @var RedBean_Adapter_DBAdapter + * @var DBAdapter */ protected $adapter; @@ -4591,7 +5516,7 @@ class RedBean_QueryWriter_PostgreSQL extends RedBean_QueryWriter_AQueryWriter im LEFT OUTER JOIN pg_class c2 ON cons.confrelid = c2.oid LEFT OUTER JOIN pg_namespace n2 ON n2.oid = c2.relnamespace WHERE c.relkind = 'r' - AND n.nspname IN ('public') + AND n.nspname = ANY( current_schemas( FALSE ) ) AND (cons.contype = 'f' OR cons.contype IS NULL) AND ( cons.conname = '{$fkCode}a' OR cons.conname = '{$fkCode}b' ) "; @@ -4600,19 +5525,20 @@ class RedBean_QueryWriter_PostgreSQL extends RedBean_QueryWriter_AQueryWriter im if ( !count( $rows ) ) { $sql1 = "ALTER TABLE \"$table\" ADD CONSTRAINT {$fkCode}a FOREIGN KEY ($property1) - REFERENCES \"$table1\" (id) ON DELETE CASCADE "; + REFERENCES \"$table1\" (id) ON DELETE CASCADE ON UPDATE CASCADE "; $sql2 = "ALTER TABLE \"$table\" ADD CONSTRAINT {$fkCode}b FOREIGN KEY ($property2) - REFERENCES \"$table2\" (id) ON DELETE CASCADE "; + REFERENCES \"$table2\" (id) ON DELETE CASCADE ON UPDATE CASCADE "; $adapter->exec( $sql1 ); $adapter->exec( $sql2 ); + return TRUE; } - return TRUE; - } catch ( Exception $e ) { + return FALSE; + } catch (\Exception $e ) { return FALSE; } } @@ -4620,9 +5546,9 @@ class RedBean_QueryWriter_PostgreSQL extends RedBean_QueryWriter_AQueryWriter im /** * Constructor * - * @param RedBean_Adapter $adapter Database Adapter + * @param Adapter $adapter Database Adapter */ - public function __construct( RedBean_Adapter $adapter ) + public function __construct( Adapter $adapter ) { $this->typeno_sqltype = array( self::C_DATATYPE_INTEGER => ' integer ', @@ -4634,6 +5560,7 @@ class RedBean_QueryWriter_PostgreSQL extends RedBean_QueryWriter_AQueryWriter im self::C_DATATYPE_SPECIAL_LSEG => ' lseg ', self::C_DATATYPE_SPECIAL_CIRCLE => ' circle ', self::C_DATATYPE_SPECIAL_MONEY => ' money ', + self::C_DATATYPE_SPECIAL_POLYGON => ' polygon ', ); $this->sqltype_typeno = array(); @@ -4657,15 +5584,15 @@ class RedBean_QueryWriter_PostgreSQL extends RedBean_QueryWriter_AQueryWriter im } /** - * @see RedBean_QueryWriter::getTables + * @see QueryWriter::getTables */ public function getTables() { - return $this->adapter->getCol( "SELECT table_name FROM information_schema.tables WHERE table_schema = 'public'" ); + return $this->adapter->getCol( 'SELECT table_name FROM information_schema.tables WHERE table_schema = ANY( current_schemas( FALSE ) )' ); } /** - * @see RedBean_QueryWriter::createTable + * @see QueryWriter::createTable */ public function createTable( $table ) { @@ -4675,7 +5602,7 @@ class RedBean_QueryWriter_PostgreSQL extends RedBean_QueryWriter_AQueryWriter im } /** - * @see RedBean_QueryWriter::getColumns + * @see QueryWriter::getColumns */ public function getColumns( $table ) { @@ -4692,7 +5619,7 @@ class RedBean_QueryWriter_PostgreSQL extends RedBean_QueryWriter_AQueryWriter im } /** - * @see RedBean_QueryWriter::scanType + * @see QueryWriter::scanType */ public function scanType( $value, $flagSpecial = FALSE ) { @@ -4700,37 +5627,37 @@ class RedBean_QueryWriter_PostgreSQL extends RedBean_QueryWriter_AQueryWriter im if ( $flagSpecial && $value ) { if ( preg_match( '/^\d{4}\-\d\d-\d\d$/', $value ) ) { - return RedBean_QueryWriter_PostgreSQL::C_DATATYPE_SPECIAL_DATE; + return PostgreSQL::C_DATATYPE_SPECIAL_DATE; } if ( preg_match( '/^\d{4}\-\d\d-\d\d\s\d\d:\d\d:\d\d(\.\d{1,6})?$/', $value ) ) { - return RedBean_QueryWriter_PostgreSQL::C_DATATYPE_SPECIAL_DATETIME; + return PostgreSQL::C_DATATYPE_SPECIAL_DATETIME; } if ( preg_match( '/^\([\d\.]+,[\d\.]+\)$/', $value ) ) { - return RedBean_QueryWriter_PostgreSQL::C_DATATYPE_SPECIAL_POINT; + return PostgreSQL::C_DATATYPE_SPECIAL_POINT; } if ( preg_match( '/^\[\([\d\.]+,[\d\.]+\),\([\d\.]+,[\d\.]+\)\]$/', $value ) ) { - return RedBean_QueryWriter_PostgreSQL::C_DATATYPE_SPECIAL_LSEG; + return PostgreSQL::C_DATATYPE_SPECIAL_LSEG; } if ( preg_match( '/^\<\([\d\.]+,[\d\.]+\),[\d\.]+\>$/', $value ) ) { - return RedBean_QueryWriter_PostgreSQL::C_DATATYPE_SPECIAL_CIRCLE; + return PostgreSQL::C_DATATYPE_SPECIAL_CIRCLE; + } + + if ( preg_match( '/^\((\([\d\.]+,[\d\.]+\),?)+\)$/', $value ) ) { + return PostgreSQL::C_DATATYPE_SPECIAL_POLYGON; } if ( preg_match( '/^\-?\$[\d,\.]+$/', $value ) ) { - return RedBean_QueryWriter_PostgreSQL::C_DATATYPE_SPECIAL_MONEY; + return PostgreSQL::C_DATATYPE_SPECIAL_MONEY; } } - $sz = ( $this->startsWithZeros( $value ) ); + if ( $this->startsWithZeros( $value ) ) return self::C_DATATYPE_TEXT; - if ( $sz ) { - return self::C_DATATYPE_TEXT; - } - - if ( $value === NULL || ( $value instanceof RedBean_Driver_PDO_NULL ) || ( is_numeric( $value ) + if ( $value === FALSE || $value === TRUE || $value === NULL || ( $value instanceof NULL ) || ( is_numeric( $value ) && floor( $value ) == $value && $value < 2147483648 && $value > -2147483648 ) @@ -4744,7 +5671,7 @@ class RedBean_QueryWriter_PostgreSQL extends RedBean_QueryWriter_AQueryWriter im } /** - * @see RedBean_QueryWriter::code + * @see QueryWriter::code */ public function code( $typedescription, $includeSpecials = FALSE ) { @@ -4752,7 +5679,7 @@ class RedBean_QueryWriter_PostgreSQL extends RedBean_QueryWriter_AQueryWriter im if ( $includeSpecials ) return $r; - if ( $r >= RedBean_QueryWriter::C_DATATYPE_RANGE_SPECIAL ) { + if ( $r >= QueryWriter::C_DATATYPE_RANGE_SPECIAL ) { return self::C_DATATYPE_SPECIFIED; } @@ -4760,7 +5687,7 @@ class RedBean_QueryWriter_PostgreSQL extends RedBean_QueryWriter_AQueryWriter im } /** - * @see RedBean_QueryWriter::widenColumn + * @see QueryWriter::widenColumn */ public function widenColumn( $type, $column, $datatype ) { @@ -4776,7 +5703,7 @@ class RedBean_QueryWriter_PostgreSQL extends RedBean_QueryWriter_AQueryWriter im } /** - * @see RedBean_QueryWriter::addUniqueIndex + * @see QueryWriter::addUniqueIndex */ public function addUniqueIndex( $table, $columns ) { @@ -4815,21 +5742,21 @@ class RedBean_QueryWriter_PostgreSQL extends RedBean_QueryWriter_AQueryWriter im } /** - * @see RedBean_QueryWriter::sqlStateIn + * @see QueryWriter::sqlStateIn */ public function sqlStateIn( $state, $list ) { $stateMap = array( - '42P01' => RedBean_QueryWriter::C_SQLSTATE_NO_SUCH_TABLE, - '42703' => RedBean_QueryWriter::C_SQLSTATE_NO_SUCH_COLUMN, - '23505' => RedBean_QueryWriter::C_SQLSTATE_INTEGRITY_CONSTRAINT_VIOLATION + '42P01' => QueryWriter::C_SQLSTATE_NO_SUCH_TABLE, + '42703' => QueryWriter::C_SQLSTATE_NO_SUCH_COLUMN, + '23505' => QueryWriter::C_SQLSTATE_INTEGRITY_CONSTRAINT_VIOLATION ); return in_array( ( isset( $stateMap[$state] ) ? $stateMap[$state] : '0' ), $list ); } /** - * @see RedBean_QueryWriter::addIndex + * @see QueryWriter::addIndex */ public function addIndex( $type, $name, $column ) { @@ -4845,78 +5772,41 @@ class RedBean_QueryWriter_PostgreSQL extends RedBean_QueryWriter_AQueryWriter im try { $this->adapter->exec( "CREATE INDEX $name ON $table ($column) " ); - } catch ( Exception $e ) { + } catch (\Exception $e ) { + //do nothing } } /** - * @see RedBean_QueryWriter::addFK + * @see QueryWriter::addFK */ public function addFK( $type, $targetType, $field, $targetField, $isDep = FALSE ) { - try { - $table = $this->esc( $type ); - $column = $this->esc( $field ); + $db = $this->adapter->getCell( 'SELECT current_database()' ); + $cfks = $this->adapter->getCell(' + SELECT constraint_name + FROM information_schema.KEY_COLUMN_USAGE + WHERE + table_catalog = ? + AND table_schema = ANY( current_schemas( FALSE ) ) + AND table_name = ? + AND column_name = ? + ', array($db, $type, $field)); - $tableNoQ = $this->esc( $type, TRUE ); - $columnNoQ = $this->esc( $field, TRUE ); - - $targetTable = $this->esc( $targetType ); - $targetTableNoQ = $this->esc( $targetType, TRUE ); - - $targetColumn = $this->esc( $targetField ); - $targetColumnNoQ = $this->esc( $targetField, TRUE ); - - $sql = "SELECT - tc.constraint_name, tc.table_name, - kcu.column_name, ccu.table_name AS foreign_table_name, - ccu.column_name AS foreign_column_name,rc.delete_rule - FROM information_schema.table_constraints AS tc - JOIN information_schema.key_column_usage AS kcu ON tc.constraint_name = kcu.constraint_name - JOIN information_schema.constraint_column_usage AS ccu ON ccu.constraint_name = tc.constraint_name - JOIN information_schema.referential_constraints AS rc ON ccu.constraint_name = rc.constraint_name - WHERE constraint_type = 'FOREIGN KEY' AND tc.table_catalog=current_database() - AND tc.table_name = '$tableNoQ' - AND ccu.table_name = '$targetTableNoQ' - AND kcu.column_name = '$columnNoQ' - AND ccu.column_name = '$targetColumnNoQ' - "; - - $row = $this->adapter->getRow( $sql ); - - $flagAddKey = FALSE; - - if ( !$row ) $flagAddKey = TRUE; - - if ( $row ) { - if ( ( $row['delete_rule'] == 'SET NULL' && $isDep ) || - ( $row['delete_rule'] != 'SET NULL' && !$isDep ) - ) { - // Delete old key and order a new one - $flagAddKey = TRUE; - $cName = $row['constraint_name']; - $sql = "ALTER TABLE $table DROP CONSTRAINT $cName "; - $this->adapter->exec( $sql ); - } - } - if ( $flagAddKey ) { + try{ + if (!$cfks) { $delRule = ( $isDep ? 'CASCADE' : 'SET NULL' ); - - $this->adapter->exec( "ALTER TABLE $table - ADD FOREIGN KEY ( $column ) REFERENCES $targetTable ( - $targetColumn) ON DELETE $delRule ON UPDATE SET NULL DEFERRABLE ;" ); - - return TRUE; + $this->adapter->exec( "ALTER TABLE {$this->esc($type)} + ADD FOREIGN KEY ( {$this->esc($field)} ) REFERENCES {$this->esc($targetType)} ( + {$this->esc($targetField)}) ON DELETE $delRule ON UPDATE $delRule DEFERRABLE ;" ); } - - return FALSE; - } catch ( Exception $e ) { + } catch (\Exception $e ) { return FALSE; } } /** - * @see RedBean_QueryWriter::wipeAll + * @see QueryWriter::wipeAll */ public function wipeAll() { @@ -4932,377 +5822,44 @@ class RedBean_QueryWriter_PostgreSQL extends RedBean_QueryWriter_AQueryWriter im } } - - -class RedBean_QueryWriter_CUBRID extends RedBean_QueryWriter_AQueryWriter implements RedBean_QueryWriter -{ - /** - * Data types - */ - const C_DATATYPE_INTEGER = 0; - const C_DATATYPE_DOUBLE = 1; - const C_DATATYPE_STRING = 2; - const C_DATATYPE_SPECIAL_DATE = 80; - const C_DATATYPE_SPECIAL_DATETIME = 81; - const C_DATATYPE_SPECIFIED = 99; - - /** - * @var RedBean_Adapter_DBAdapter - */ - protected $adapter; - - /** - * @var string - */ - protected $quoteCharacter = '`'; - - /** - * Obtains the keys of a table using the PDO schema function. - * - * @param string $table - * - * @return array - */ - protected function getKeys( $table, $table2 = NULL ) - { - $pdo = $this->adapter->getDatabase()->getPDO(); - - $keys = $pdo->cubrid_schema( PDO::CUBRID_SCH_EXPORTED_KEYS, $table ); - - if ( $table2 ) { - $keys = array_merge( $keys, $pdo->cubrid_schema( PDO::CUBRID_SCH_IMPORTED_KEYS, $table2 ) ); - } - - return $keys; - } - - /** - * Add the constraints for a specific database driver: CUBRID - * - * @param string $table table - * @param string $table1 table1 - * @param string $table2 table2 - * @param string $property1 property1 - * @param string $property2 property2 - * - * @return boolean - */ - protected function constrain( $table, $table1, $table2, $property1, $property2 ) - { - $this->buildFK( $table, $table1, $property1, 'id', TRUE ); - $this->buildFK( $table, $table2, $property2, 'id', TRUE ); - } - - /** - * This method adds a foreign key from type and field to - * target type and target field. - * The foreign key is created without an action. On delete/update - * no action will be triggered. The FK is only used to allow database - * tools to generate pretty diagrams and to make it easy to add actions - * later on. - * This methods accepts a type and infers the corresponding table name. - * - * - * @param string $type type that will have a foreign key field - * @param string $targetType points to this type - * @param string $field field that contains the foreign key value - * @param string $targetField field where the fk points to - * - * @return void - */ - protected function buildFK( $type, $targetType, $field, $targetField, $isDep = FALSE ) - { - $table = $this->esc( $type ); - $tableNoQ = $this->esc( $type, TRUE ); - - $targetTable = $this->esc( $targetType ); - $targetTableNoQ = $this->esc( $targetType, TRUE ); - - $column = $this->esc( $field ); - $columnNoQ = $this->esc( $field, TRUE ); - - $targetColumn = $this->esc( $targetField ); - - $keys = $this->getKeys( $targetTableNoQ, $tableNoQ ); - - $needsToDropFK = FALSE; - - foreach ( $keys as $key ) { - if ( $key['FKTABLE_NAME'] == $tableNoQ && $key['FKCOLUMN_NAME'] == $columnNoQ ) { - // Already has an FK - $needsToDropFK = TRUE; - - if ( ( $isDep && $key['DELETE_RULE'] == 0 ) || ( !$isDep && $key['DELETE_RULE'] == 3 ) ) { - return; - } - - break; - } - } - - if ( $needsToDropFK ) { - $sql = "ALTER TABLE $table DROP FOREIGN KEY {$key['FK_NAME']} "; - - $this->adapter->exec( $sql ); - } - - $casc = ( $isDep ? 'CASCADE' : 'SET NULL' ); - - $sql = "ALTER TABLE $table ADD CONSTRAINT FOREIGN KEY($column) REFERENCES $targetTable($targetColumn) ON DELETE $casc "; - - $this->adapter->exec( $sql ); - } - - /** - * Constructor - * - * @param RedBean_Adapter $adapter Database Adapter - */ - public function __construct( RedBean_Adapter $adapter ) - { - $this->typeno_sqltype = array( - RedBean_QueryWriter_CUBRID::C_DATATYPE_INTEGER => ' INTEGER ', - RedBean_QueryWriter_CUBRID::C_DATATYPE_DOUBLE => ' DOUBLE ', - RedBean_QueryWriter_CUBRID::C_DATATYPE_STRING => ' STRING ', - RedBean_QueryWriter_CUBRID::C_DATATYPE_SPECIAL_DATE => ' DATE ', - RedBean_QueryWriter_CUBRID::C_DATATYPE_SPECIAL_DATETIME => ' DATETIME ', - ); - - $this->sqltype_typeno = array(); - - foreach ( $this->typeno_sqltype as $k => $v ) { - $this->sqltype_typeno[trim( ( $v ) )] = $k; - } - - $this->sqltype_typeno['STRING(1073741823)'] = self::C_DATATYPE_STRING; - - $this->adapter = $adapter; - } - - /** - * This method returns the datatype to be used for primary key IDS and - * foreign keys. Returns one if the data type constants. - * - * @return integer $const data type to be used for IDS. - */ - public function getTypeForID() - { - return self::C_DATATYPE_INTEGER; - } - - /** - * @see RedBean_QueryWriter::getTables - */ - public function getTables() - { - $rows = $this->adapter->getCol( "SELECT class_name FROM db_class WHERE is_system_class = 'NO';" ); - - return $rows; - } - - /** - * @see RedBean_QueryWriter::createTable - */ - public function createTable( $table ) - { - $sql = 'CREATE TABLE ' - . $this->esc( $table ) - . ' ("id" integer AUTO_INCREMENT, CONSTRAINT "pk_' - . $this->esc( $table, TRUE ) - . '_id" PRIMARY KEY("id"))'; - - $this->adapter->exec( $sql ); - } - - /** - * @see RedBean_QueryWriter::getColumns - */ - public function getColumns( $table ) - { - $table = $this->esc( $table ); - - $columnsRaw = $this->adapter->get( "SHOW COLUMNS FROM $table" ); - - $columns = array(); - foreach ( $columnsRaw as $r ) { - $columns[$r['Field']] = $r['Type']; - } - - return $columns; - } - - /** - * @see RedBean_QueryWriter::scanType - */ - public function scanType( $value, $flagSpecial = FALSE ) - { - $this->svalue = $value; - - if ( is_null( $value ) ) { - return self::C_DATATYPE_INTEGER; - } - - if ( $flagSpecial ) { - if ( preg_match( '/^\d{4}\-\d\d-\d\d$/', $value ) ) { - return self::C_DATATYPE_SPECIAL_DATE; - } - if ( preg_match( '/^\d{4}\-\d\d-\d\d\s\d\d:\d\d:\d\d$/', $value ) ) { - return self::C_DATATYPE_SPECIAL_DATETIME; - } - } - - $value = strval( $value ); - - if ( !$this->startsWithZeros( $value ) ) { - if ( is_numeric( $value ) && ( floor( $value ) == $value ) && $value >= -2147483647 && $value <= 2147483647 ) { - return self::C_DATATYPE_INTEGER; - } - if ( is_numeric( $value ) ) { - return self::C_DATATYPE_DOUBLE; - } - } - - return self::C_DATATYPE_STRING; - } - - /** - * @see RedBean_QueryWriter::code - */ - public function code( $typedescription, $includeSpecials = FALSE ) - { - $r = ( ( isset( $this->sqltype_typeno[$typedescription] ) ) ? $this->sqltype_typeno[$typedescription] : self::C_DATATYPE_SPECIFIED ); - - if ( $includeSpecials ) { - return $r; - } - - if ( $r >= RedBean_QueryWriter::C_DATATYPE_RANGE_SPECIAL ) { - return self::C_DATATYPE_SPECIFIED; - } - - return $r; - } - - /** - * @see RedBean_QueryWriter::addColumn - */ - public function addColumn( $type, $column, $field ) - { - $table = $type; - $type = $field; - - $table = $this->esc( $table ); - $column = $this->esc( $column ); - - $type = array_key_exists( $type, $this->typeno_sqltype ) ? $this->typeno_sqltype[$type] : ''; - - $this->adapter->exec( "ALTER TABLE $table ADD COLUMN $column $type " ); - } - - /** - * @see RedBean_QueryWriter::addUniqueIndex - */ - public function addUniqueIndex( $table, $columns ) - { - $table = $this->esc( $table ); - - sort( $columns ); // else we get multiple indexes due to order-effects - - foreach ( $columns as $k => $v ) { - $columns[$k] = $this->esc( $v ); - } - - $r = $this->adapter->get( "SHOW INDEX FROM $table" ); - - $name = 'UQ_' . sha1( implode( ',', $columns ) ); - - if ( $r ) { - foreach ( $r as $i ) { - if ( strtoupper( $i['Key_name'] ) == strtoupper( $name ) ) { - return; - } - } - } - - $sql = "ALTER TABLE $table ADD CONSTRAINT UNIQUE $name (" . implode( ',', $columns ) . ")"; - - $this->adapter->exec( $sql ); - } - - /** - * @see RedBean_QueryWriter::sqlStateIn - */ - public function sqlStateIn( $state, $list ) - { - return ( $state == 'HY000' ) ? ( count( array_diff( array( - RedBean_QueryWriter::C_SQLSTATE_INTEGRITY_CONSTRAINT_VIOLATION, - RedBean_QueryWriter::C_SQLSTATE_NO_SUCH_COLUMN, - RedBean_QueryWriter::C_SQLSTATE_NO_SUCH_TABLE - ), $list ) ) !== 3 ) : FALSE; - } - - /** - * @see RedBean_QueryWriter::addIndex - */ - public function addIndex( $type, $name, $column ) - { - $table = $type; - $table = $this->esc( $table ); - - $name = preg_replace( '/\W/', '', $name ); - - $column = $this->esc( $column ); - - $index = $this->adapter->getRow( "SELECT 1 as `exists` FROM db_index WHERE index_name = ? ", array( $name ) ); - - if ( $index && $index['exists'] ) { - return; // positive number will return, 0 will continue. - } - - try { - $this->adapter->exec( "CREATE INDEX $name ON $table ($column) " ); - } catch ( Exception $e ) { - } - } - - /** - * @see RedBean_QueryWriter::addFK - */ - public function addFK( $type, $targetType, $field, $targetField, $isDependent = FALSE ) - { - $this->buildFK( $type, $targetType, $field, $targetField, $isDependent ); - } - - /** - * @see RedBean_QueryWriter::wipeAll - */ - public function wipeAll() - { - foreach ( $this->getTables() as $t ) { - foreach ( $this->getKeys( $t ) as $k ) { - $this->adapter->exec( "ALTER TABLE \"{$k['FKTABLE_NAME']}\" DROP FOREIGN KEY \"{$k['FK_NAME']}\"" ); - } - - $this->adapter->exec( "DROP TABLE \"$t\"" ); - } - } - - /** - * @see RedBean_QueryWriter::esc - */ - public function esc( $dbStructure, $noQuotes = FALSE ) - { - return parent::esc( strtolower( $dbStructure ), $noQuotes ); - } -} - - -class RedBean_Exception extends LogicException +} + +namespace RedBeanPHP { + +/** + * RedBean\Exception Base + * + * @file RedBean/Exception.php + * @desc Represents the base class for RedBean\Exceptions + * @author Gabor de Mooij and the RedBeanPHP Community + * @license BSD/GPLv2 + * + * copyright (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community + * This source file is subject to the BSD/GPLv2 License that is bundled + * with this source code in the file license.txt. + */ +class RedException extends \Exception { } +} +namespace RedBeanPHP\RedException { -class RedBean_Exception_SQL extends RuntimeException +use RedBeanPHP\RedException as RedException; + +/** + * RedBean\Exception SQL + * + * @file RedBean/Exception/SQL.php + * @desc Represents a generic database exception independent of the underlying driver. + * @author Gabor de Mooij and the RedBeanPHP Community + * @license BSD/GPLv2 + * + * (c) copyright G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community. + * This source file is subject to the BSD/GPLv2 License that is bundled + * with this source code in the file license.txt. + */ +class SQL extends RedException { /** @@ -5340,30 +5897,47 @@ class RedBean_Exception_SQL extends RuntimeException */ public function __toString() { - return '[' . $this->getSQLState() . '] - ' . $this->getMessage().PHP_EOL. - 'trace: ' . $this->getTraceAsString(); + return '[' . $this->getSQLState() . '] - ' . $this->getMessage()."\n". + 'trace: ' . $this->getTraceAsString(); } } - - -class RedBean_Exception_Security extends RedBean_Exception -{ } +namespace RedBeanPHP { -class RedBean_OODB extends RedBean_Observable +use RedBeanPHP\OODBBean as OODBBean; +use RedBeanPHP\Observable as Observable; +use RedBeanPHP\Adapter\DBAdapter as DBAdapter; +use RedBeanPHP\BeanHelper\FacadeBeanHelper as FacadeBeanHelper; +use RedBeanPHP\QueryWriter as QueryWriter; +use RedBeanPHP\RedException\Security as Security; +use RedBeanPHP\SimpleModel as SimpleModel; +use RedBeanPHP\BeanHelper as BeanHelper; +use RedBeanPHP\RedException\SQL as SQL; +use RedBeanPHP\QueryWriter\AQueryWriter as AQueryWriter; +use RedBeanPHP\OODB as OODB; + +/** + * Abstract Repository + * + * @file RedBean/Repository.php + * @desc RedBean Object Database + * @author Gabor de Mooij and the RedBeanPHP community + * @license BSD/GPLv2 + * + * OODB manages two repositories, a fluid one that + * adjust the database schema on-the-fly to accomodate for + * new bean types (tables) and new properties (columns) and + * a frozen one for use in a production environment. OODB + * allows you to swap the repository instances using the freeze() + * method. + * + * copyright (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community + * This source file is subject to the BSD/GPLv2 License that is bundled + * with this source code in the file license.txt. + */ +abstract class Repository { - - /** - * @var array - */ - protected $chillList = array(); - - /** - * @var array - */ - protected $dep = array(); - /** * @var array */ @@ -5375,67 +5949,10 @@ class RedBean_OODB extends RedBean_Observable protected $nesting = 0; /** - * @var RedBean_Adapter_DBAdapter + * @var DBAdapter */ protected $writer; - /** - * @var boolean - */ - protected $isFrozen = FALSE; - - /** - * @var RedBean_BeanHelper_Facade - */ - protected $beanhelper = NULL; - - /** - * @var RedBean_AssociationManager - */ - protected $assocManager = NULL; - - /** - * Handles Exceptions. Suppresses exceptions caused by missing structures. - * - * @param Exception $exception exception - * - * @return void - * - * @throws Exception - */ - private function handleException( Exception $exception ) - { - if ( !$this->writer->sqlStateIn( $exception->getSQLState(), - array( - RedBean_QueryWriter::C_SQLSTATE_NO_SUCH_TABLE, - RedBean_QueryWriter::C_SQLSTATE_NO_SUCH_COLUMN ) ) - ) { - throw $exception; - } - } - - /** - * Unboxes a bean from a FUSE model if needed and checks whether the bean is - * an instance of RedBean_OODBBean. - * - * @param RedBean_OODBBean $bean bean you wish to unbox - * - * @return RedBean_OODBBean - * - * @throws RedBean_Exception_Security - */ - private function unboxIfNeeded( $bean ) - { - if ( $bean instanceof RedBean_SimpleModel ) { - $bean = $bean->unbox(); - } - if ( !( $bean instanceof RedBean_OODBBean ) ) { - throw new RedBean_Exception_Security( 'OODB Store requires a bean, got: ' . gettype( $bean ) ); - } - - return $bean; - } - /** * Process groups. Internal function. Processes different kind of groups for * storage function. Given a list of original beans and a list of current beans, @@ -5451,7 +5968,7 @@ class RedBean_OODB extends RedBean_Observable * * @return array */ - private function processGroups( $originals, $current, $additions, $trashcan, $residue ) + protected function processGroups( $originals, $current, $additions, $trashcan, $residue ) { return array( array_merge( $additions, array_diff( $current, $originals ) ), @@ -5460,38 +5977,14 @@ class RedBean_OODB extends RedBean_Observable ); } - /** - * Figures out the desired type given the cast string ID. - * - * @param string $cast cast identifier - * - * @return integer - * - * @throws RedBean_Exception_Security - */ - private function getTypeFromCast( $cast ) - { - if ( $cast == 'string' ) { - $typeno = $this->writer->scanType( 'STRING' ); - } elseif ( $cast == 'id' ) { - $typeno = $this->writer->getTypeForID(); - } elseif ( isset( $this->writer->sqltype_typeno[$cast] ) ) { - $typeno = $this->writer->sqltype_typeno[$cast]; - } else { - throw new RedBean_Exception_Security( 'Invalid Cast' ); - } - - return $typeno; - } - /** * Processes an embedded bean. * - * @param RedBean_OODBBean|RedBean_SimpleModel $embeddedBean the bean or model + * @param OODBBean|SimpleModel $embeddedBean the bean or model * * @return integer */ - private function prepareEmbeddedBean( $embeddedBean ) + protected function prepareEmbeddedBean( $embeddedBean ) { if ( !$embeddedBean->id || $embeddedBean->getMeta( 'tainted' ) ) { $this->store( $embeddedBean ); @@ -5500,155 +5993,24 @@ class RedBean_OODB extends RedBean_Observable return $embeddedBean->id; } - /** - * Orders the Query Writer to create a table if it does not exist already and - * adds a note in the build report about the creation. - * - * @param RedBean_OODBBean $bean bean to update report of - * @param string $table table to check and create if not exists - * - * @return void - */ - private function createTableIfNotExists( RedBean_OODBBean $bean, $table ) - { - //Does table exist? If not, create - if ( !$this->isFrozen && !$this->tableExists( $this->writer->esc( $table, TRUE ) ) ) { - $this->writer->createTable( $table ); - $bean->setMeta( 'buildreport.flags.created', TRUE ); - } - } - - /** - * Adds the unique constraints described in the meta data. - * - * @param RedBean_OODBBean $bean bean - * - * @return void - */ - private function addUniqueConstraints( RedBean_OODBBean $bean ) - { - if ( $uniques = $bean->getMeta( 'buildcommand.unique' ) ) { - $table = $bean->getMeta( 'type' ); - foreach ( $uniques as $unique ) { - $this->writer->addUniqueIndex( $table, $unique ); - } - } - } - - /** - * Stores a cleaned bean; i.e. only scalar values. This is the core of the store() - * method. When all lists and embedded beans (parent objects) have been processed and - * removed from the original bean the bean is passed to this method to be stored - * in the database. - * - * @param RedBean_OODBBean $bean the clean bean - * - * @return void - */ - private function storeBean( RedBean_OODBBean $bean ) - { - if ( !$this->isFrozen ) { - $this->check( $bean ); - } - $table = $bean->getMeta( 'type' ); - if ( $bean->getMeta( 'tainted' ) ) { - $this->createTableIfNotExists( $bean, $table ); - $updateValues = $this->getUpdateValues( $bean ); - if ( !$this->isFrozen ) { - $this->addUniqueConstraints( $bean ); - } - $bean->id = $this->writer->updateRecord( $table, $updateValues, $bean->id ); - $bean->setMeta( 'tainted', FALSE ); - } - } - - /** - * Returns a structured array of update values using the following format: - * array( - * property => $property, - * value => $value - * ); - * - * @param RedBean_OODBBean $bean bean to extract update values from - * - * @return array - */ - private function getUpdateValues( RedBean_OODBBean $bean ) - { - $updateValues = array(); - foreach ( $bean as $property => $value ) { - if ( !$this->isFrozen && $property !== 'id' ) { - $this->moldTable( $bean, $property, $value ); - } - if ( $property !== 'id' ) { - $updateValues[] = array( 'property' => $property, 'value' => $value ); - } - } - - return $updateValues; - } - - /** - * Molds the table to fit the bean data. - * Given a property and a value and the bean, this method will - * adjust the table structure to fit the requirements of the property and value. - * This may include adding a new column or widening an existing column to hold a larger - * or different kind of value. This method employs the writer to adjust the table - * structure in the database. Schema updates are recorded in meta properties of the bean. - * - * @param RedBean_OODBBean $bean bean to get cast data from and store meta in - * @param string $property property to store - * @param mixed $value value to store - * - * @return void - */ - private function moldTable( RedBean_OODBBean $bean, $property, $value ) - { - $table = $bean->getMeta( 'type' ); - $columns = $this->writer->getColumns( $table ); - if ( !in_array( $bean->getMeta( 'type' ), $this->chillList ) ) { - if ( $bean->getMeta( "cast.$property", -1 ) !== -1 ) { //check for explicitly specified types - $cast = $bean->getMeta( "cast.$property" ); - $typeno = $this->getTypeFromCast( $cast ); - } else { - $cast = FALSE; - $typeno = $this->writer->scanType( $value, TRUE ); - } - if ( isset( $columns[$this->writer->esc( $property, TRUE )] ) ) { //Is this property represented in the table ? - if ( !$cast ) { //rescan without taking into account special types >80 - $typeno = $this->writer->scanType( $value, FALSE ); - } - $sqlt = $this->writer->code( $columns[$this->writer->esc( $property, TRUE )] ); - if ( $typeno > $sqlt ) { //no, we have to widen the database column type - $this->writer->widenColumn( $table, $property, $typeno ); - $bean->setMeta( 'buildreport.flags.widen', TRUE ); - } - } else { - $this->writer->addColumn( $table, $property, $typeno ); - $bean->setMeta( 'buildreport.flags.addcolumn', TRUE ); - $this->processBuildCommands( $table, $property, $bean ); - } - } - } - /** * Processes a list of beans from a bean. A bean may contain lists. This * method handles shared addition lists; i.e. the $bean->sharedObject properties. * - * @param RedBean_OODBBean $bean the bean + * @param OODBBean $bean the bean * @param array $sharedAdditions list with shared additions * * @return void * - * @throws RedBean_Exception_Security + * @throws Security */ - private function processSharedAdditions( $bean, $sharedAdditions ) + protected function processSharedAdditions( $bean, $sharedAdditions ) { foreach ( $sharedAdditions as $addition ) { - if ( $addition instanceof RedBean_OODBBean ) { - $this->assocManager->associate( $addition, $bean ); + if ( $addition instanceof OODBBean ) { + $this->oodb->getAssociationManager()->associate( $addition, $bean ); } else { - throw new RedBean_Exception_Security( 'Array may only contain RedBean_OODBBeans' ); + throw new RedException( 'Array may only contain OODBBeans' ); } } } @@ -5660,12 +6022,12 @@ class RedBean_OODB extends RedBean_Observable * checks if there have been any modification to this bean, in that case * the bean is stored once again, otherwise the bean will be left untouched. * - * @param RedBean_OODBBean $bean the bean + * @param OODBBean $bean the bean * @param array $ownresidue list * * @return void */ - private function processResidue( $ownresidue ) + protected function processResidue( $ownresidue ) { foreach ( $ownresidue as $residue ) { if ( $residue->getMeta( 'tainted' ) ) { @@ -5683,19 +6045,21 @@ class RedBean_OODB extends RedBean_Observable * If not, the connection between the bean and the owner bean will be broken by * setting the ID to NULL. * - * @param RedBean_OODBBean $bean the bean + * @param OODBBean $bean the bean * @param array $ownTrashcan list * * @return void */ - private function processTrashcan( $bean, $ownTrashcan ) + protected function processTrashcan( $bean, $ownTrashcan ) { + foreach ( $ownTrashcan as $trash ) { + $myFieldLink = $bean->getMeta( 'type' ) . '_id'; $alias = $bean->getMeta( 'sys.alias.' . $trash->getMeta( 'type' ) ); if ( $alias ) $myFieldLink = $alias . '_id'; - - if ( isset( $this->dep[$trash->getMeta( 'type' )] ) && in_array( $bean->getMeta( 'type' ), $this->dep[$trash->getMeta( 'type' )] ) ) { + + if ( $trash->getMeta( 'sys.garbage' ) === true ) { $this->trash( $trash ); } else { $trash->$myFieldLink = NULL; @@ -5707,105 +6071,43 @@ class RedBean_OODB extends RedBean_Observable /** * Unassociates the list items in the trashcan. * - * @param RedBean_OODBBean $bean bean + * @param OODBBean $bean bean * @param array $sharedTrashcan list * * @return void */ - private function processSharedTrashcan( $bean, $sharedTrashcan ) + protected function processSharedTrashcan( $bean, $sharedTrashcan ) { foreach ( $sharedTrashcan as $trash ) { - $this->assocManager->unassociate( $trash, $bean ); + $this->oodb->getAssociationManager()->unassociate( $trash, $bean ); } } /** * Stores all the beans in the residue group. * - * @param RedBean_OODBBean $bean bean + * @param OODBBean $bean bean * @param array $sharedresidue list * * @return void */ - private function processSharedResidue( $bean, $sharedresidue ) + protected function processSharedResidue( $bean, $sharedresidue ) { foreach ( $sharedresidue as $residue ) { $this->store( $residue ); } } - /** - * Processes embedded beans. - * Each embedded bean will be indexed and foreign keys will - * be created if the bean is in the dependency list. - * - * @param RedBean_OODBBean $bean bean - * @param array $embeddedBeans embedded beans - * - * @return void - */ - private function addForeignKeysForParentBeans( $bean, $embeddedBeans ) - { - foreach ( $embeddedBeans as $linkField => $embeddedBean ) { - $this->writer->addIndex( $bean->getMeta( 'type' ), - 'index_foreignkey_' . $bean->getMeta( 'type' ) . '_' . $embeddedBean->getMeta( 'type' ), - $linkField ); - $isDep = $this->isDependentOn( $bean->getMeta( 'type' ), $embeddedBean->getMeta( 'type' ) ); - $this->writer->addFK( $bean->getMeta( 'type' ), $embeddedBean->getMeta( 'type' ), $linkField, 'id', $isDep ); - } - } - - /** - * Part of the store() functionality. - * Handles all new additions after the bean has been saved. - * Stores addition bean in own-list, extracts the id and - * adds a foreign key. Also adds a constraint in case the type is - * in the dependent list. - * - * @param RedBean_OODBBean $bean bean - * @param array $ownAdditions list of addition beans in own-list - * - * @return void - * - * @throws RedBean_Exception_Security - */ - private function processAdditions( $bean, $ownAdditions ) - { - $beanType = $bean->getMeta( 'type' ); - - foreach ( $ownAdditions as $addition ) { - if ( $addition instanceof RedBean_OODBBean ) { - - $myFieldLink = $beanType . '_id'; - $alias = $bean->getMeta( 'sys.alias.' . $addition->getMeta( 'type' ) ); - if ( $alias ) $myFieldLink = $alias . '_id'; - - $addition->$myFieldLink = $bean->id; - $addition->setMeta( 'cast.' . $myFieldLink, 'id' ); - $this->store( $addition ); - if ( !$this->isFrozen ) { - $this->writer->addIndex( $addition->getMeta( 'type' ), - 'index_foreignkey_' . $addition->getMeta( 'type' ) . '_' . $bean->getMeta( 'type' ), - $myFieldLink ); - $isDep = $this->isDependentOn( $addition->getMeta( 'type' ), $bean->getMeta( 'type' ) ); - $this->writer->addFK( $addition->getMeta( 'type' ), $bean->getMeta( 'type' ), $myFieldLink, 'id', $isDep ); - } - } else { - throw new RedBean_Exception_Security( 'Array may only contain RedBean_OODBBeans' ); - } - } - } - /** * Determines whether the bean has 'loaded lists' or * 'loaded embedded beans' that need to be processed * by the store() method. * - * @param RedBean_OODBBean $bean bean to be examined + * @param OODBBean $bean bean to be examined * * @return boolean */ - private function hasListsOrObjects( RedBean_OODBBean $bean ) + protected function hasListsOrObjects( OODBBean $bean ) { $processLists = FALSE; foreach ( $bean as $value ) { @@ -5818,216 +6120,63 @@ class RedBean_OODB extends RedBean_Observable return $processLists; } - /** - * Checks whether reference type has been marked as dependent on target type. - * This is the result of setting reference type as a key in R::dependencies() and - * putting target type in its array. - * - * @param string $refType reference type - * @param string $otherType other type / target type - * - * @return boolean - */ - private function isDependentOn( $refType, $otherType ) - { - return (boolean) ( isset( $this->dep[$refType] ) && in_array( $otherType, $this->dep[$refType] ) ); - } - - /** - * Processes all column based build commands. - * A build command is an additional instruction for the Query Writer. It is processed only when - * a column gets created. The build command is often used to instruct the writer to write some - * extra SQL to create indexes or constraints. Build commands are stored in meta data of the bean. - * They are only for internal use, try to refrain from using them in your code directly. - * - * @param string $table name of the table to process build commands for - * @param string $property name of the property to process build commands for - * @param RedBean_OODBBean $bean bean that contains the build commands - * - * @return void - */ - private function processBuildCommands( $table, $property, RedBean_OODBBean $bean ) - { - if ( $inx = ( $bean->getMeta( 'buildcommand.indexes' ) ) ) { - if ( isset( $inx[$property] ) ) { - $this->writer->addIndex( $table, $inx[$property], $property ); - } - } - } /** * Converts an embedded bean to an ID, removed the bean property and * stores the bean in the embedded beans array. * * @param array $embeddedBeans destination array for embedded bean - * @param RedBean_OODBBean $bean target bean + * @param OODBBean $bean target bean * @param string $property property that contains the embedded bean - * @param RedBean_OODBBean $value embedded bean itself + * @param OODBBean $value embedded bean itself */ - private function processEmbeddedBean( &$embeddedBeans, $bean, $property, RedBean_OODBBean $value ) + protected function processEmbeddedBean( &$embeddedBeans, $bean, $property, OODBBean $value ) { $linkField = $property . '_id'; - $bean->$linkField = $this->prepareEmbeddedBean( $value ); + $id = $this->prepareEmbeddedBean( $value ); + if ($bean->$linkField != $id) $bean->$linkField = $id; $bean->setMeta( 'cast.' . $linkField, 'id' ); $embeddedBeans[$linkField] = $value; - $bean->removeProperty( $property ); + unset( $bean->$property ); } - /** - * Stores a bean and its lists in one run. - * - * @param RedBean_OODBBean $bean - * - * @return void - */ - private function processLists( RedBean_OODBBean $bean ) - { - $sharedAdditions = $sharedTrashcan = $sharedresidue = $sharedItems = $ownAdditions = $ownTrashcan = $ownresidue = $embeddedBeans = array(); //Define groups - foreach ( $bean as $property => $value ) { - $value = ( $value instanceof RedBean_SimpleModel ) ? $value->unbox() : $value; - if ( $value instanceof RedBean_OODBBean ) { - $this->processEmbeddedBean( $embeddedBeans, $bean, $property, $value ); - } elseif ( is_array( $value ) ) { - $originals = $bean->getMeta( 'sys.shadow.' . $property, array() ); - if ( strpos( $property, 'own' ) === 0 ) { - list( $ownAdditions, $ownTrashcan, $ownresidue ) = $this->processGroups( $originals, $value, $ownAdditions, $ownTrashcan, $ownresidue ); - $bean->removeProperty( $property ); - } elseif ( strpos( $property, 'shared' ) === 0 ) { - list( $sharedAdditions, $sharedTrashcan, $sharedresidue ) = $this->processGroups( $originals, $value, $sharedAdditions, $sharedTrashcan, $sharedresidue ); - $bean->removeProperty( $property ); - } - } - } - $this->storeBean( $bean ); - if ( !$this->isFrozen ) { - $this->addForeignKeysForParentBeans( $bean, $embeddedBeans ); - } - $this->processTrashcan( $bean, $ownTrashcan ); - $this->processAdditions( $bean, $ownAdditions ); - $this->processResidue( $ownresidue ); - $this->processSharedTrashcan( $bean, $sharedTrashcan ); - $this->processSharedAdditions( $bean, $sharedAdditions ); - $this->processSharedResidue( $bean, $sharedresidue ); - } /** * Constructor, requires a query writer. * - * @param RedBean_QueryWriter $writer writer + * @param QueryWriter $writer writer */ - public function __construct( RedBean_QueryWriter $writer ) + public function __construct( OODB $oodb, QueryWriter $writer ) { - if ( $writer instanceof RedBean_QueryWriter ) { - $this->writer = $writer; - } - $this->beanhelper = new RedBean_BeanHelper_Facade(); + $this->writer = $writer; + $this->oodb = $oodb; } /** - * Toggles fluid or frozen mode. In fluid mode the database - * structure is adjusted to accomodate your objects. In frozen mode - * this is not the case. - * - * You can also pass an array containing a selection of frozen types. - * Let's call this chilly mode, it's just like fluid mode except that - * certain types (i.e. tables) aren't touched. - * - * @param boolean|array $toggle TRUE if you want to use OODB instance in frozen mode - * - * @return void - */ - public function freeze( $toggle ) - { - if ( is_array( $toggle ) ) { - $this->chillList = $toggle; - $this->isFrozen = FALSE; - } else { - $this->isFrozen = (boolean) $toggle; - } - } - - /** - * Returns the current mode of operation of RedBean. - * In fluid mode the database - * structure is adjusted to accomodate your objects. - * In frozen mode - * this is not the case. - * - * @return boolean - */ - public function isFrozen() - { - return (bool) $this->isFrozen; - } - - /** - * Dispenses a new bean (a RedBean_OODBBean Bean Object) - * of the specified type. Always - * use this function to get an empty bean object. Never - * instantiate a RedBean_OODBBean yourself because it needs - * to be configured before you can use it with RedBean. This - * function applies the appropriate initialization / - * configuration for you. - * - * @param string $type type of bean you want to dispense - * @param string $number number of beans you would like to get - * - * @return RedBean_OODBBean - */ - public function dispense( $type, $number = 1 ) - { - $beans = array(); - for ( $i = 0; $i < $number; $i++ ) { - $bean = new RedBean_OODBBean; - $bean->initializeForDispense( $type, $this->beanhelper ); - if ( !$this->isFrozen ) { - $this->check( $bean ); - } - $this->signal( 'dispense', $bean ); - $beans[] = $bean; - } - - return ( count( $beans ) === 1 ) ? array_pop( $beans ) : $beans; - } - - /** - * Sets bean helper to be given to beans. - * Bean helpers assist beans in getting a reference to a toolbox. - * - * @param RedBean_BeanHelper $beanhelper helper - * - * @return void - */ - public function setBeanHelper( RedBean_BeanHelper $beanhelper ) - { - $this->beanhelper = $beanhelper; - } - - /** - * Checks whether a RedBean_OODBBean bean is valid. + * Checks whether a OODBBean bean is valid. * If the type is not valid or the ID is not valid it will - * throw an exception: RedBean_Exception_Security. + * throw an exception: Security. * - * @param RedBean_OODBBean $bean the bean that needs to be checked + * @param OODBBean $bean the bean that needs to be checked * * @return void * - * @throws RedBean_Exception_Security $exception + * @throws Security $exception */ - public function check( RedBean_OODBBean $bean ) + public function check( OODBBean $bean ) { //Is all meta information present? if ( !isset( $bean->id ) ) { - throw new RedBean_Exception_Security( 'Bean has incomplete Meta Information id ' ); + throw new RedException( 'Bean has incomplete Meta Information id ' ); } if ( !( $bean->getMeta( 'type' ) ) ) { - throw new RedBean_Exception_Security( 'Bean has incomplete Meta Information II' ); + throw new RedException( 'Bean has incomplete Meta Information II' ); } //Pattern of allowed characters $pattern = '/[^a-z0-9_]/i'; //Does the type contain invalid characters? if ( preg_match( $pattern, $bean->getMeta( 'type' ) ) ) { - throw new RedBean_Exception_Security( 'Bean Type is invalid' ); + throw new RedException( 'Bean Type is invalid' ); } //Are the properties and values valid? foreach ( $bean as $prop => $value ) { @@ -6035,12 +6184,12 @@ class RedBean_OODB extends RedBean_Observable is_array( $value ) || ( is_object( $value ) ) ) { - throw new RedBean_Exception_Security( "Invalid Bean value: property $prop" ); + throw new RedException( "Invalid Bean value: property $prop" ); } else if ( strlen( $prop ) < 1 || preg_match( $pattern, $prop ) ) { - throw new RedBean_Exception_Security( "Invalid Bean property: property $prop" ); + throw new RedException( "Invalid Bean property: property $prop" ); } } } @@ -6069,7 +6218,7 @@ class RedBean_OODB extends RedBean_Observable * * @return array * - * @throws RedBean_Exception_SQL + * @throws SQL */ public function find( $type, $conditions = array(), $sql = NULL, $bindings = array() ) { @@ -6084,31 +6233,16 @@ class RedBean_OODB extends RedBean_Observable $beans = $this->convertToBeans( $type, $this->writer->queryRecord( $type, $conditions, $sql, $bindings ) ); return $beans; - } catch ( RedBean_Exception_SQL $exception ) { + } catch ( SQL $exception ) { $this->handleException( $exception ); } return array(); } - /** - * Checks whether the specified table already exists in the database. - * Not part of the Object Database interface! - * - * @deprecated Use RedBean_QueryWriter_AQueryWriter::typeExists() instead. - * - * @param string $table table name - * - * @return boolean - */ - public function tableExists( $table ) - { - return $this->writer->tableExists( $table ); - } - /** * Stores a bean in the database. This method takes a - * RedBean_OODBBean Bean Object $bean and stores it + * OODBBean Bean Object $bean and stores it * in the database. If the database schema is not compatible * with this bean and RedBean runs in fluid mode the schema * will be altered to store the bean correctly. @@ -6116,140 +6250,36 @@ class RedBean_OODB extends RedBean_Observable * RedBean runs in frozen mode it will throw an exception. * This function returns the primary key ID of the inserted * bean. - * + * * The return value is an integer if possible. If it is not possible to * represent the value as an integer a string will be returned. We use - * explicit casts instead of functions to preserve performance + * explicit casts instead of functions to preserve performance * (0.13 vs 0.28 for 10000 iterations on Core i3). * - * @param RedBean_OODBBean|RedBean_SimpleModel $bean bean to store + * @param OODBBean|SimpleModel $bean bean to store * * @return integer|string * - * @throws RedBean_Exception_Security + * @throws Security */ public function store( $bean ) { - $bean = $this->unboxIfNeeded( $bean ); $processLists = $this->hasListsOrObjects( $bean ); if ( !$processLists && !$bean->getMeta( 'tainted' ) ) { return $bean->getID(); //bail out! } - $this->signal( 'update', $bean ); + $this->oodb->signal( 'update', $bean ); $processLists = $this->hasListsOrObjects( $bean ); //check again, might have changed by model! if ( $processLists ) { $this->processLists( $bean ); } else { $this->storeBean( $bean ); } - $this->signal( 'after_update', $bean ); + $this->oodb->signal( 'after_update', $bean ); return ( (string) $bean->id === (string) (int) $bean->id ) ? (int) $bean->id : (string) $bean->id; } - /** - * Loads a bean from the object database. - * It searches for a RedBean_OODBBean Bean Object in the - * database. It does not matter how this bean has been stored. - * RedBean uses the primary key ID $id and the string $type - * to find the bean. The $type specifies what kind of bean you - * are looking for; this is the same type as used with the - * dispense() function. If RedBean finds the bean it will return - * the RedBean_OODB Bean object; if it cannot find the bean - * RedBean will return a new bean of type $type and with - * primary key ID 0. In the latter case it acts basically the - * same as dispense(). - * - * Important note: - * If the bean cannot be found in the database a new bean of - * the specified type will be generated and returned. - * - * @param string $type type of bean you want to load - * @param integer $id ID of the bean you want to load - * - * @throws RedBean_Exception_SQL - * - * @return RedBean_OODBBean - * - */ - public function load( $type, $id ) - { - $bean = $this->dispense( $type ); - if ( isset( $this->stash[$this->nesting][$id] ) ) { - $row = $this->stash[$this->nesting][$id]; - } else { - try { - $rows = $this->writer->queryRecord( $type, array( 'id' => array( $id ) ) ); - } catch ( RedBean_Exception_SQL $exception ) { - if ( $this->writer->sqlStateIn( $exception->getSQLState(), - array( - RedBean_QueryWriter::C_SQLSTATE_NO_SUCH_COLUMN, - RedBean_QueryWriter::C_SQLSTATE_NO_SUCH_TABLE ) - ) - ) { - $rows = 0; - if ( $this->isFrozen ) { - throw $exception; //only throw if frozen - } - } - } - if ( empty( $rows ) ) { - return $bean; - } - $row = array_pop( $rows ); - } - $bean->importRow( $row ); - $this->nesting++; - $this->signal( 'open', $bean ); - $this->nesting--; - - return $bean->setMeta( 'tainted', FALSE ); - } - - /** - * Removes a bean from the database. - * This function will remove the specified RedBean_OODBBean - * Bean Object from the database. - * - * @param RedBean_OODBBean|RedBean_SimpleModel $bean bean you want to remove from database - * - * @return void - * - * @throws RedBean_Exception_Security - */ - public function trash( $bean ) - { - if ( $bean instanceof RedBean_SimpleModel ) { - $bean = $bean->unbox(); - } - if ( !( $bean instanceof RedBean_OODBBean ) ) { - throw new RedBean_Exception_Security( 'OODB Store requires a bean, got: ' . gettype( $bean ) ); - } - $this->signal( 'delete', $bean ); - foreach ( $bean as $property => $value ) { - if ( $value instanceof RedBean_OODBBean ) { - $bean->removeProperty( $property ); - } - if ( is_array( $value ) ) { - if ( strpos( $property, 'own' ) === 0 ) { - $bean->removeProperty( $property ); - } elseif ( strpos( $property, 'shared' ) === 0 ) { - $bean->removeProperty( $property ); - } - } - } - if ( !$this->isFrozen ) { - $this->check( $bean ); - } - try { - $this->writer->deleteRecord( $bean->getMeta( 'type' ), array( 'id' => array( $bean->id ) ), NULL ); - } catch ( RedBean_Exception_SQL $exception ) { - $this->handleException( $exception ); - } - $bean->id = 0; - $this->signal( 'after_delete', $bean ); - } - /** * Returns an array of beans. Pass a type and a series of ids and * this method will bring you the corresponding beans. @@ -6272,7 +6302,7 @@ class RedBean_OODB extends RedBean_Observable $collection = array(); try { $rows = $this->writer->queryRecord( $type, array( 'id' => $ids ) ); - } catch ( RedBean_Exception_SQL $e ) { + } catch ( SQL $e ) { $this->handleException( $e ); $rows = FALSE; } @@ -6327,14 +6357,21 @@ class RedBean_OODB extends RedBean_Observable * * @return integer * - * @throws RedBean_Exception_SQL + * @throws SQL */ public function count( $type, $addSQL = '', $bindings = array() ) { + $type = AQueryWriter::camelsSnake( $type ); + if ( count( explode( '_', $type ) ) > 2 ) { + throw new RedException( 'Invalid type for count.' ); + } + try { return (int) $this->writer->queryRecordCount( $type, array(), $addSQL, $bindings ); - } catch ( RedBean_Exception_SQL $exception ) { - if ( !$this->writer->sqlStateIn( $exception->getSQLState(), array( RedBean_QueryWriter::C_SQLSTATE_NO_SUCH_TABLE ) ) ) { + } catch ( SQL $exception ) { + if ( !$this->writer->sqlStateIn( $exception->getSQLState(), array( + QueryWriter::C_SQLSTATE_NO_SUCH_TABLE, + QueryWriter::C_SQLSTATE_NO_SUCH_COLUMN ) ) ) { throw $exception; } } @@ -6342,6 +6379,56 @@ class RedBean_OODB extends RedBean_Observable return 0; } + /** + * Removes a bean from the database. + * This function will remove the specified OODBBean + * Bean Object from the database. + * + * @param OODBBean|SimpleModel $bean bean you want to remove from database + * + * @return void + * + * @throws Security + */ + public function trash( $bean ) + { + $this->oodb->signal( 'delete', $bean ); + foreach ( $bean as $property => $value ) { + if ( $value instanceof OODBBean ) { + unset( $bean->$property ); + } + if ( is_array( $value ) ) { + if ( strpos( $property, 'own' ) === 0 ) { + unset( $bean->$property ); + } elseif ( strpos( $property, 'shared' ) === 0 ) { + unset( $bean->$property ); + } + } + } + try { + $this->writer->deleteRecord( $bean->getMeta( 'type' ), array( 'id' => array( $bean->id ) ), NULL ); + } catch ( SQL $exception ) { + $this->handleException( $exception ); + } + $bean->id = 0; + $this->oodb->signal( 'after_delete', $bean ); + } + + /** + * Checks whether the specified table already exists in the database. + * Not part of the Object Database interface! + * + * @deprecated Use AQueryWriter::typeExists() instead. + * + * @param string $table table name + * + * @return boolean + */ + public function tableExists( $table ) + { + return $this->writer->tableExists( $table ); + } + /** * Trash all beans of a given type. Wipes an entire type of bean. * @@ -6349,7 +6436,7 @@ class RedBean_OODB extends RedBean_Observable * * @return boolean * - * @throws RedBean_Exception_SQL + * @throws SQL */ public function wipe( $type ) { @@ -6357,8 +6444,8 @@ class RedBean_OODB extends RedBean_Observable $this->writer->wipe( $type ); return TRUE; - } catch ( RedBean_Exception_SQL $exception ) { - if ( !$this->writer->sqlStateIn( $exception->getSQLState(), array( RedBean_QueryWriter::C_SQLSTATE_NO_SUCH_TABLE ) ) ) { + } catch ( SQL $exception ) { + if ( !$this->writer->sqlStateIn( $exception->getSQLState(), array( QueryWriter::C_SQLSTATE_NO_SUCH_TABLE ) ) ) { throw $exception; } @@ -6366,19 +6453,1154 @@ class RedBean_OODB extends RedBean_Observable } } + +} +} + +namespace RedBeanPHP\Repository { + +use RedBeanPHP\OODBBean as OODBBean; +use RedBeanPHP\Observable as Observable; +use RedBeanPHP\Adapter\DBAdapter as DBAdapter; +use RedBeanPHP\BeanHelper\FacadeBeanHelper as FacadeBeanHelper; +use RedBeanPHP\QueryWriter as QueryWriter; +use RedBeanPHP\RedException as RedException; +use RedBeanPHP\RedException\Security as Security; +use RedBeanPHP\SimpleModel as SimpleModel; +use RedBeanPHP\BeanHelper as BeanHelper; +use RedBeanPHP\RedException\SQL as SQL; +use RedBeanPHP\QueryWriter\AQueryWriter as AQueryWriter; +use RedBeanPHP\Repository as Repository; + +/** + * Fluid Repository + * + * @file RedBean/Repository/Fluid.php + * @desc RedBean Object Database + * @author Gabor de Mooij and the RedBeanPHP community + * @license BSD/GPLv2 + * + * OODB manages two repositories, a fluid one that + * adjust the database schema on-the-fly to accomodate for + * new bean types (tables) and new properties (columns) and + * a frozen one for use in a production environment. OODB + * allows you to swap the repository instances using the freeze() + * method. + * + * copyright (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community + * This source file is subject to the BSD/GPLv2 License that is bundled + * with this source code in the file license.txt. + */ +class Fluid extends Repository +{ + + /** + * Figures out the desired type given the cast string ID. + * + * @param string $cast cast identifier + * + * @return integer + * + * @throws Security + */ + private function getTypeFromCast( $cast ) + { + if ( $cast == 'string' ) { + $typeno = $this->writer->scanType( 'STRING' ); + } elseif ( $cast == 'id' ) { + $typeno = $this->writer->getTypeForID(); + } elseif ( isset( $this->writer->sqltype_typeno[$cast] ) ) { + $typeno = $this->writer->sqltype_typeno[$cast]; + } else { + throw new RedException( 'Invalid Cast' ); + } + + return $typeno; + } + + /** + * Orders the Query Writer to create a table if it does not exist already and + * adds a note in the build report about the creation. + * + * @param OODBBean $bean bean to update report of + * @param string $table table to check and create if not exists + * + * @return void + */ + private function createTableIfNotExists( OODBBean $bean, $table ) + { + //Does table exist? If not, create + if ( !$this->tableExists( $this->writer->esc( $table, TRUE ) ) ) { + $this->writer->createTable( $table ); + $bean->setMeta( 'buildreport.flags.created', TRUE ); + } + } + + /** + * Processes all column based build commands. + * A build command is an additional instruction for the Query Writer. It is processed only when + * a column gets created. The build command is often used to instruct the writer to write some + * extra SQL to create indexes or constraints. Build commands are stored in meta data of the bean. + * They are only for internal use, try to refrain from using them in your code directly. + * + * @param string $table name of the table to process build commands for + * @param string $property name of the property to process build commands for + * @param OODBBean $bean bean that contains the build commands + * + * @return void + */ + private function processBuildCommands( $table, $property, OODBBean $bean ) + { + if ( $inx = ( $bean->getMeta( 'buildcommand.indexes' ) ) ) { + if ( isset( $inx[$property] ) ) { + $this->writer->addIndex( $table, $inx[$property], $property ); + } + } + } + + /** + * Adds the unique constraints described in the meta data. + * + * @param OODBBean $bean bean + * + * @return void + */ + private function addUniqueConstraints( OODBBean $bean ) + { + if ( $uniques = $bean->getMeta( 'buildcommand.unique' ) ) { + $table = $bean->getMeta( 'type' ); + foreach ( $uniques as $unique ) { + if ( !$this->oodb->isChilled( $table ) ) $this->writer->addUniqueIndex( $table, $unique ); + } + } + } + + /** + * Molds the table to fit the bean data. + * Given a property and a value and the bean, this method will + * adjust the table structure to fit the requirements of the property and value. + * This may include adding a new column or widening an existing column to hold a larger + * or different kind of value. This method employs the writer to adjust the table + * structure in the database. Schema updates are recorded in meta properties of the bean. + * + * @param OODBBean $bean bean to get cast data from and store meta in + * @param string $property property to store + * @param mixed $value value to store + * + * @return void + */ + private function moldTable( OODBBean $bean, $property, $value ) + { + $table = $bean->getMeta( 'type' ); + $columns = $this->writer->getColumns( $table ); + if ( !$this->oodb->isChilled( $bean->getMeta( 'type' ) ) ) { + if ( $bean->getMeta( "cast.$property", -1 ) !== -1 ) { //check for explicitly specified types + $cast = $bean->getMeta( "cast.$property" ); + $typeno = $this->getTypeFromCast( $cast ); + } else { + $cast = FALSE; + $typeno = $this->writer->scanType( $value, TRUE ); + } + if ( isset( $columns[$this->writer->esc( $property, TRUE )] ) ) { //Is this property represented in the table ? + if ( !$cast ) { //rescan without taking into account special types >80 + $typeno = $this->writer->scanType( $value, FALSE ); + } + $sqlt = $this->writer->code( $columns[$this->writer->esc( $property, TRUE )] ); + if ( $typeno > $sqlt ) { //no, we have to widen the database column type + $this->writer->widenColumn( $table, $property, $typeno ); + $bean->setMeta( 'buildreport.flags.widen', TRUE ); + } + } else { + $this->writer->addColumn( $table, $property, $typeno ); + $bean->setMeta( 'buildreport.flags.addcolumn', TRUE ); + $this->processBuildCommands( $table, $property, $bean ); + } + } + } + + /** + * Processes embedded beans. + * Each embedded bean will be indexed and foreign keys will + * be created if the bean is in the dependency list. + * + * @param OODBBean $bean bean + * @param array $embeddedBeans embedded beans + * + * @return void + */ + private function addForeignKeysForParentBeans( $bean, $embeddedBeans ) + { + $cachedIndex = array(); + foreach ( $embeddedBeans as $linkField => $embeddedBean ) { + $beanType = $bean->getMeta( 'type' ); + $embeddedType = $embeddedBean->getMeta( 'type' ); + $key = $beanType . '|' . $embeddedType . '>' . $linkField; + if ( !isset( $cachedIndex[$key] ) ) { + $this->writer->addIndex( $bean->getMeta( 'type' ), + 'index_foreignkey_' . $beanType . '_' . $embeddedType, + $linkField ); + $this->writer->addFK( $beanType, $embeddedType, $linkField, 'id', FALSE ); + $cachedIndex[$key] = TRUE; + } + } + } + + /** + * Part of the store() functionality. + * Handles all new additions after the bean has been saved. + * Stores addition bean in own-list, extracts the id and + * adds a foreign key. Also adds a constraint in case the type is + * in the dependent list. + * + * @param OODBBean $bean bean + * @param array $ownAdditions list of addition beans in own-list + * + * @return void + * + * @throws Security + */ + private function processAdditions( $bean, $ownAdditions ) + { + $beanType = $bean->getMeta( 'type' ); + + $cachedIndex = array(); + foreach ( $ownAdditions as $addition ) { + if ( $addition instanceof OODBBean ) { + + $myFieldLink = $beanType . '_id'; + $alias = $bean->getMeta( 'sys.alias.' . $addition->getMeta( 'type' ) ); + if ( $alias ) $myFieldLink = $alias . '_id'; + + $addition->$myFieldLink = $bean->id; + $addition->setMeta( 'cast.' . $myFieldLink, 'id' ); + $this->store( $addition ); + $additionType = $addition->getMeta( 'type' ); + $key = $additionType . '|' . $beanType . '>' . $myFieldLink; + if ( !isset( $cachedIndex[$key] ) ) { + $this->writer->addIndex( $additionType, + 'index_foreignkey_' . $additionType . '_' . $beanType, + $myFieldLink ); + $isDep = $bean->getMeta( 'sys.exclusive-'.$additionType ); + $this->writer->addFK( $additionType, $beanType, $myFieldLink, 'id', $isDep ); + $cachedIndex[$key] = TRUE; + } + + } else { + throw new RedException( 'Array may only contain OODBBeans' ); + } + } + } + + /** + * Stores a cleaned bean; i.e. only scalar values. This is the core of the store() + * method. When all lists and embedded beans (parent objects) have been processed and + * removed from the original bean the bean is passed to this method to be stored + * in the database. + * + * @param OODBBean $bean the clean bean + * + * @return void + */ + protected function storeBean( OODBBean $bean ) + { + if ( $bean->getMeta( 'changed' ) ) { + $this->check( $bean ); + $table = $bean->getMeta( 'type' ); + $this->createTableIfNotExists( $bean, $table ); + $updateValues = $this->getUpdateValues( $bean ); + $this->addUniqueConstraints( $bean ); + $bean->id = $this->writer->updateRecord( $table, $updateValues, $bean->id ); + $bean->setMeta( 'changed', FALSE ); + } + $bean->setMeta( 'tainted', FALSE ); + } + + /** + * Returns a structured array of update values using the following format: + * array( + * property => $property, + * value => $value + * ); + * + * @param OODBBean $bean bean to extract update values from + * + * @return array + */ + protected function getUpdateValues( OODBBean $bean ) + { + $updateValues = array(); + foreach ( $bean as $property => $value ) { + if ( $property !== 'id' ) { + $this->moldTable( $bean, $property, $value ); + } + if ( $property !== 'id' ) { + $updateValues[] = array( 'property' => $property, 'value' => $value ); + } + } + + return $updateValues; + } + + /** + * Handles\Exceptions. Suppresses exceptions caused by missing structures. + * + * @param\Exception $exception exception + * + * @return void + * + * @throws\Exception + */ + protected function handleException( \Exception $exception ) + { + if ( !$this->writer->sqlStateIn( $exception->getSQLState(), + array( + QueryWriter::C_SQLSTATE_NO_SUCH_TABLE, + QueryWriter::C_SQLSTATE_NO_SUCH_COLUMN ) ) + ) { + throw $exception; + } + } + + /** + * Stores a bean and its lists in one run. + * + * @param OODBBean $bean + * + * @return void + */ + protected function processLists( OODBBean $bean ) + { + $sharedAdditions = $sharedTrashcan = $sharedresidue = $sharedItems = $ownAdditions = $ownTrashcan = $ownresidue = $embeddedBeans = array(); //Define groups + foreach ( $bean as $property => $value ) { + $value = ( $value instanceof SimpleModel ) ? $value->unbox() : $value; + if ( $value instanceof OODBBean ) { + $this->processEmbeddedBean( $embeddedBeans, $bean, $property, $value ); + } elseif ( is_array( $value ) ) { + $originals = $bean->getMeta( 'sys.shadow.' . $property, array() ); + $bean->setMeta( 'sys.shadow.' . $property, NULL ); //clear shadow + if ( strpos( $property, 'own' ) === 0 ) { + list( $ownAdditions, $ownTrashcan, $ownresidue ) = $this->processGroups( $originals, $value, $ownAdditions, $ownTrashcan, $ownresidue ); + $listName = lcfirst( substr( $property, 3 ) ); + if ($bean->getMeta( 'sys.exclusive-'. $listName ) ) { + OODBBean::setMetaAll( $ownTrashcan, 'sys.garbage', TRUE ); + } + unset( $bean->$property ); + } elseif ( strpos( $property, 'shared' ) === 0 ) { + list( $sharedAdditions, $sharedTrashcan, $sharedresidue ) = $this->processGroups( $originals, $value, $sharedAdditions, $sharedTrashcan, $sharedresidue ); + unset( $bean->$property ); + } + } + } + $this->storeBean( $bean ); + $this->addForeignKeysForParentBeans( $bean, $embeddedBeans ); + $this->processTrashcan( $bean, $ownTrashcan ); + $this->processAdditions( $bean, $ownAdditions ); + $this->processResidue( $ownresidue ); + $this->processSharedTrashcan( $bean, $sharedTrashcan ); + $this->processSharedAdditions( $bean, $sharedAdditions ); + $this->processSharedResidue( $bean, $sharedresidue ); + } + + + /** + * Dispenses a new bean (a OODBBean Bean Object) + * of the specified type. Always + * use this function to get an empty bean object. Never + * instantiate a OODBBean yourself because it needs + * to be configured before you can use it with RedBean. This + * function applies the appropriate initialization / + * configuration for you. + * + * @param string $type type of bean you want to dispense + * @param string $number number of beans you would like to get + * @param boolean $alwaysReturnArray if TRUE always returns the result as an array + * + * @return OODBBean + */ + public function dispense( $type, $number = 1, $alwaysReturnArray = FALSE ) + { + $OODBBEAN = defined( 'REDBEAN_OODBBEAN_CLASS' ) ? REDBEAN_OODBBEAN_CLASS : '\RedBeanPHP\OODBBean'; + $beans = array(); + for ( $i = 0; $i < $number; $i++ ) { + $bean = new $OODBBEAN; + $bean->initializeForDispense( $type, $this->oodb->getBeanHelper() ); + $this->check( $bean ); + $this->oodb->signal( 'dispense', $bean ); + $beans[] = $bean; + } + + return ( count( $beans ) === 1 && !$alwaysReturnArray ) ? array_pop( $beans ) : $beans; + } + + /** + * Loads a bean from the object database. + * It searches for a OODBBean Bean Object in the + * database. It does not matter how this bean has been stored. + * RedBean uses the primary key ID $id and the string $type + * to find the bean. The $type specifies what kind of bean you + * are looking for; this is the same type as used with the + * dispense() function. If RedBean finds the bean it will return + * the OODB Bean object; if it cannot find the bean + * RedBean will return a new bean of type $type and with + * primary key ID 0. In the latter case it acts basically the + * same as dispense(). + * + * Important note: + * If the bean cannot be found in the database a new bean of + * the specified type will be generated and returned. + * + * @param string $type type of bean you want to load + * @param integer $id ID of the bean you want to load + * + * @throws SQL + * + * @return OODBBean + * + */ + public function load( $type, $id ) + { + $bean = $this->dispense( $type ); + if ( isset( $this->stash[$this->nesting][$id] ) ) { + $row = $this->stash[$this->nesting][$id]; + } else { + try { + $rows = $this->writer->queryRecord( $type, array( 'id' => array( $id ) ) ); + } catch ( SQL $exception ) { + if ( $this->writer->sqlStateIn( $exception->getSQLState(), + array( + QueryWriter::C_SQLSTATE_NO_SUCH_COLUMN, + QueryWriter::C_SQLSTATE_NO_SUCH_TABLE ) + ) + ) { + $rows = 0; + + } + } + if ( empty( $rows ) ) { + return $bean; + } + $row = array_pop( $rows ); + } + $bean->importRow( $row ); + $this->nesting++; + $this->oodb->signal( 'open', $bean ); + $this->nesting--; + + return $bean->setMeta( 'tainted', FALSE ); + } +} +} + +namespace RedBeanPHP\Repository { + +use RedBeanPHP\OODBBean as OODBBean; +use RedBeanPHP\Observable as Observable; +use RedBeanPHP\Adapter\DBAdapter as DBAdapter; +use RedBeanPHP\BeanHelper\FacadeBeanHelper as FacadeBeanHelper; +use RedBeanPHP\QueryWriter as QueryWriter; +use RedBeanPHP\RedException as RedException; +use RedBeanPHP\RedException\Security as Security; +use RedBeanPHP\SimpleModel as SimpleModel; +use RedBeanPHP\BeanHelper as BeanHelper; +use RedBeanPHP\RedException\SQL as SQL; +use RedBeanPHP\QueryWriter\AQueryWriter as AQueryWriter; +use RedBeanPHP\Repository as Repository; + +/** + * Frozen Repository + * + * @file RedBean/Repository/Frozen.php + * @desc RedBean Object Database + * @author Gabor de Mooij and the RedBeanPHP community + * @license BSD/GPLv2 + * + * OODB manages two repositories, a fluid one that + * adjust the database schema on-the-fly to accomodate for + * new bean types (tables) and new properties (columns) and + * a frozen one for use in a production environment. OODB + * allows you to swap the repository instances using the freeze() + * method. + * + * copyright (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community + * This source file is subject to the BSD/GPLv2 License that is bundled + * with this source code in the file license.txt. + */ +class Frozen extends Repository +{ + /** + * Handles\Exceptions. Suppresses exceptions caused by missing structures. + * + * @param \Exception $exception exception + * + * @return void + * + * @throws \Exception + */ + protected function handleException( \Exception $exception ) + { + throw $exception; + } + + /** + * Stores a cleaned bean; i.e. only scalar values. This is the core of the store() + * method. When all lists and embedded beans (parent objects) have been processed and + * removed from the original bean the bean is passed to this method to be stored + * in the database. + * + * @param OODBBean $bean the clean bean + * + * @return void + */ + protected function storeBean( OODBBean $bean ) + { + if ( $bean->getMeta( 'changed' ) ) { + + list( $properties, $table ) = $bean->getPropertiesAndType(); + $id = $properties['id']; + unset($properties['id']); + $updateValues = array(); + $k1 = 'property'; + $k2 = 'value'; + foreach( $properties as $key => $value ) { + $updateValues[] = array( $k1 => $key, $k2 => $value ); + } + $bean->id = $this->writer->updateRecord( $table, $updateValues, $id ); + $bean->setMeta( 'changed', FALSE ); + } + $bean->setMeta( 'tainted', FALSE ); + } + + /** + * Part of the store() functionality. + * Handles all new additions after the bean has been saved. + * Stores addition bean in own-list, extracts the id and + * adds a foreign key. Also adds a constraint in case the type is + * in the dependent list. + * + * @param OODBBean $bean bean + * @param array $ownAdditions list of addition beans in own-list + * + * @return void + * + * @throws Security + */ + protected function processAdditions( $bean, $ownAdditions ) + { + $beanType = $bean->getMeta( 'type' ); + + $cachedIndex = array(); + foreach ( $ownAdditions as $addition ) { + if ( $addition instanceof OODBBean ) { + + $myFieldLink = $beanType . '_id'; + $alias = $bean->getMeta( 'sys.alias.' . $addition->getMeta( 'type' ) ); + if ( $alias ) $myFieldLink = $alias . '_id'; + + $addition->$myFieldLink = $bean->id; + $addition->setMeta( 'cast.' . $myFieldLink, 'id' ); + $this->store( $addition ); + + } else { + throw new RedException( 'Array may only contain OODBBeans' ); + } + } + } + + /** + * Stores a bean and its lists in one run. + * + * @param OODBBean $bean + * + * @return void + */ + protected function processLists( OODBBean $bean ) + { + $sharedAdditions = $sharedTrashcan = $sharedresidue = $sharedItems = $ownAdditions = $ownTrashcan = $ownresidue = $embeddedBeans = array(); //Define groups + foreach ( $bean as $property => $value ) { + $value = ( $value instanceof SimpleModel ) ? $value->unbox() : $value; + if ( $value instanceof OODBBean ) { + $this->processEmbeddedBean( $embeddedBeans, $bean, $property, $value ); + } elseif ( is_array( $value ) ) { + $originals = $bean->getMeta( 'sys.shadow.' . $property, array() ); + $bean->setMeta( 'sys.shadow.' . $property, NULL ); //clear shadow + if ( strpos( $property, 'own' ) === 0 ) { + list( $ownAdditions, $ownTrashcan, $ownresidue ) = $this->processGroups( $originals, $value, $ownAdditions, $ownTrashcan, $ownresidue ); + $listName = lcfirst( substr( $property, 3 ) ); + if ($bean->getMeta( 'sys.exclusive-'. $listName ) ) { + OODBBean::setMetaAll( $ownTrashcan, 'sys.garbage', TRUE ); + } + unset( $bean->$property ); + } elseif ( strpos( $property, 'shared' ) === 0 ) { + list( $sharedAdditions, $sharedTrashcan, $sharedresidue ) = $this->processGroups( $originals, $value, $sharedAdditions, $sharedTrashcan, $sharedresidue ); + unset( $bean->$property ); + } + } + } + $this->storeBean( $bean ); + $this->processTrashcan( $bean, $ownTrashcan ); + $this->processAdditions( $bean, $ownAdditions ); + $this->processResidue( $ownresidue ); + $this->processSharedTrashcan( $bean, $sharedTrashcan ); + $this->processSharedAdditions( $bean, $sharedAdditions ); + $this->processSharedResidue( $bean, $sharedresidue ); + } + + /** + * Dispenses a new bean (a OODBBean Bean Object) + * of the specified type. Always + * use this function to get an empty bean object. Never + * instantiate a OODBBean yourself because it needs + * to be configured before you can use it with RedBean. This + * function applies the appropriate initialization / + * configuration for you. + * + * @param string $type type of bean you want to dispense + * @param string $number number of beans you would like to get + * @param boolean $alwaysReturnArray if TRUE always returns the result as an array + * + * @return OODBBean + */ + public function dispense( $type, $number = 1, $alwaysReturnArray = FALSE ) + { + $OODBBEAN = defined( 'REDBEAN_OODBBEAN_CLASS' ) ? REDBEAN_OODBBEAN_CLASS : '\RedBeanPHP\OODBBean'; + $beans = array(); + for ( $i = 0; $i < $number; $i++ ) { + $bean = new $OODBBEAN; + $bean->initializeForDispense( $type, $this->oodb->getBeanHelper() ); + $this->oodb->signal( 'dispense', $bean ); + $beans[] = $bean; + } + + return ( count( $beans ) === 1 && !$alwaysReturnArray ) ? array_pop( $beans ) : $beans; + } + + /** + * Loads a bean from the object database. + * It searches for a OODBBean Bean Object in the + * database. It does not matter how this bean has been stored. + * RedBean uses the primary key ID $id and the string $type + * to find the bean. The $type specifies what kind of bean you + * are looking for; this is the same type as used with the + * dispense() function. If RedBean finds the bean it will return + * the OODB Bean object; if it cannot find the bean + * RedBean will return a new bean of type $type and with + * primary key ID 0. In the latter case it acts basically the + * same as dispense(). + * + * Important note: + * If the bean cannot be found in the database a new bean of + * the specified type will be generated and returned. + * + * @param string $type type of bean you want to load + * @param integer $id ID of the bean you want to load + * + * @throws SQL + * + * @return OODBBean + * + */ + public function load( $type, $id ) + { + $bean = $this->dispense( $type ); + if ( isset( $this->stash[$this->nesting][$id] ) ) { + $row = $this->stash[$this->nesting][$id]; + } else { + try { + $rows = $this->writer->queryRecord( $type, array( 'id' => array( $id ) ) ); + } catch ( SQL $exception ) { + if ( $this->writer->sqlStateIn( $exception->getSQLState(), + array( + QueryWriter::C_SQLSTATE_NO_SUCH_COLUMN, + QueryWriter::C_SQLSTATE_NO_SUCH_TABLE ) + ) + ) { + throw $exception; //only throw if frozen + + } + } + if ( empty( $rows ) ) { + return $bean; + } + $row = array_pop( $rows ); + } + $bean->importRow( $row ); + $this->nesting++; + $this->oodb->signal( 'open', $bean ); + $this->nesting--; + + return $bean->setMeta( 'tainted', FALSE ); + } +} +} + +namespace RedBeanPHP { + +use RedBeanPHP\OODBBean as OODBBean; +use RedBeanPHP\Observable as Observable; +use RedBeanPHP\Adapter\DBAdapter as DBAdapter; +use RedBeanPHP\BeanHelper\FacadeBeanHelper as FacadeBeanHelper; +use RedBeanPHP\AssociationManager as AssociationManager; +use RedBeanPHP\QueryWriter as QueryWriter; +use RedBeanPHP\RedException\Security as Security; +use RedBeanPHP\SimpleModel as SimpleModel; +use RedBeanPHP\BeanHelper as BeanHelper; +use RedBeanPHP\RedException\SQL as SQL; +use RedBeanPHP\QueryWriter\AQueryWriter as AQueryWriter; +use RedBeanPHP\Repository as Repository; +use RedBeanPHP\Repository\Fluid as FluidRepo; +use RedBeanPHP\Repository\Frozen as FrozenRepo; + + +/** + * RedBean Object Oriented DataBase + * + * @file RedBean/OODB.php + * @desc RedBean Object Database + * @author Gabor de Mooij and the RedBeanPHP community + * @license BSD/GPLv2 + * + * The RedBean OODB Class is the main class of RedBeanPHP. + * It takes OODBBean objects and stores them to and loads them from the + * database as well as providing other CRUD functions. This class acts as a + * object database. + * + * copyright (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community + * This source file is subject to the BSD/GPLv2 License that is bundled + * with this source code in the file license.txt. + */ +class OODB extends Observable +{ + /** + * @var array + */ + private static $sqlFilters = array(); + + /** + * @var array + */ + protected $chillList = array(); + + + /** + * @var array + */ + protected $stash = NULL; + + /* + * @var integer + */ + protected $nesting = 0; + + /** + * @var DBAdapter + */ + protected $writer; + + /** + * @var boolean + */ + protected $isFrozen = FALSE; + + /** + * @var FacadeBeanHelper + */ + protected $beanhelper = NULL; + + /** + * @var AssociationManager + */ + protected $assocManager = NULL; + + /** + * @var Repository + */ + protected $repository = NULL; + + /** + * @var FrozenRepo + */ + protected $frozenRepository = NULL; + + /** + * @var FluidRepo + */ + protected $fluidRepository = NULL; + + /** + * Unboxes a bean from a FUSE model if needed and checks whether the bean is + * an instance of OODBBean. + * + * @param OODBBean $bean bean you wish to unbox + * + * @return OODBBean + * + * @throws Security + */ + protected function unboxIfNeeded( $bean ) + { + if ( $bean instanceof SimpleModel ) { + $bean = $bean->unbox(); + } + if ( !( $bean instanceof OODBBean ) ) { + throw new RedException( 'OODB Store requires a bean, got: ' . gettype( $bean ) ); + } + + return $bean; + } + + /** + * Constructor, requires a query writer. + * + * @param QueryWriter $writer writer + */ + public function __construct( QueryWriter $writer ) + { + if ( $writer instanceof QueryWriter ) { + $this->writer = $writer; + } + + $this->freeze( FALSE ); + } + + /** + * Toggles fluid or frozen mode. In fluid mode the database + * structure is adjusted to accomodate your objects. In frozen mode + * this is not the case. + * + * You can also pass an array containing a selection of frozen types. + * Let's call this chilly mode, it's just like fluid mode except that + * certain types (i.e. tables) aren't touched. + * + * @param boolean|array $toggle TRUE if you want to use OODB instance in frozen mode + * + * @return void + */ + public function freeze( $toggle ) + { + if ( is_array( $toggle ) ) { + $this->chillList = $toggle; + $this->isFrozen = FALSE; + } else { + $this->isFrozen = (boolean) $toggle; + } + + if ( $this->isFrozen ) { + if ( !$this->frozenRepository ) { + $this->frozenRepository = new FrozenRepo( $this, $this->writer ); + } + + $this->repository = $this->frozenRepository; + + } else { + if ( !$this->fluidRepository ) { + $this->fluidRepository = new FluidRepo( $this, $this->writer ); + } + + $this->repository = $this->fluidRepository; + } + + if ( count( self::$sqlFilters ) ) { + AQueryWriter::setSQLFilters( self::$sqlFilters, ( !$this->isFrozen ) ); + } + + } + + /** + * Returns the current mode of operation of RedBean. + * In fluid mode the database + * structure is adjusted to accomodate your objects. + * In frozen mode + * this is not the case. + * + * @return boolean + */ + public function isFrozen() + { + return (bool) $this->isFrozen; + } + + /** + * Determines whether a type is in the chill list. + * If a type is 'chilled' it's frozen, so its schema cannot be + * changed anymore. However other bean types may still be modified. + * This method is a convenience method for other objects to check if + * the schema of a certain type is locked for modification. + * + * @param string $type the type you wish to check + * + * @return boolean + */ + public function isChilled( $type ) + { + return (boolean) ( in_array( $type, $this->chillList ) ); + } + + /** + * Dispenses a new bean (a OODBBean Bean Object) + * of the specified type. Always + * use this function to get an empty bean object. Never + * instantiate a OODBBean yourself because it needs + * to be configured before you can use it with RedBean. This + * function applies the appropriate initialization / + * configuration for you. + * + * @param string $type type of bean you want to dispense + * @param string $number number of beans you would like to get + * @param boolean $alwaysReturnArray if TRUE always returns the result as an array + * + * @return OODBBean + */ + public function dispense( $type, $number = 1, $alwaysReturnArray = FALSE ) + { + if ( $number < 1 ) { + if ( $alwaysReturnArray ) return array(); + return NULL; + } + + return $this->repository->dispense( $type, $number, $alwaysReturnArray ); + } + + /** + * Sets bean helper to be given to beans. + * Bean helpers assist beans in getting a reference to a toolbox. + * + * @param BeanHelper $beanhelper helper + * + * @return void + */ + public function setBeanHelper( BeanHelper $beanhelper ) + { + $this->beanhelper = $beanhelper; + } + + /** + * Returns the current bean helper. + * Bean helpers assist beans in getting a reference to a toolbox. + * + * @return BeanHelper + */ + public function getBeanHelper() + { + return $this->beanhelper; + } + + /** + * Checks whether a OODBBean bean is valid. + * If the type is not valid or the ID is not valid it will + * throw an exception: Security. + * + * @param OODBBean $bean the bean that needs to be checked + * + * @return void + * + * @throws Security $exception + */ + public function check( OODBBean $bean ) + { + $this->repository->check( $bean ); + } + + /** + * Searches the database for a bean that matches conditions $conditions and sql $addSQL + * and returns an array containing all the beans that have been found. + * + * Conditions need to take form: + * + * array( + * 'PROPERTY' => array( POSSIBLE VALUES... 'John', 'Steve' ) + * 'PROPERTY' => array( POSSIBLE VALUES... ) + * ); + * + * All conditions are glued together using the AND-operator, while all value lists + * are glued using IN-operators thus acting as OR-conditions. + * + * Note that you can use property names; the columns will be extracted using the + * appropriate bean formatter. + * + * @param string $type type of beans you are looking for + * @param array $conditions list of conditions + * @param string $addSQL SQL to be used in query + * @param array $bindings whether you prefer to use a WHERE clause or not (TRUE = not) + * + * @return array + * + * @throws SQL + */ + public function find( $type, $conditions = array(), $sql = NULL, $bindings = array() ) + { + return $this->repository->find( $type, $conditions, $sql, $bindings ); + } + + /** + * Checks whether the specified table already exists in the database. + * Not part of the Object Database interface! + * + * @deprecated Use AQueryWriter::typeExists() instead. + * + * @param string $table table name + * + * @return boolean + */ + public function tableExists( $table ) + { + return $this->repository->tableExists( $table ); + } + + /** + * Stores a bean in the database. This method takes a + * OODBBean Bean Object $bean and stores it + * in the database. If the database schema is not compatible + * with this bean and RedBean runs in fluid mode the schema + * will be altered to store the bean correctly. + * If the database schema is not compatible with this bean and + * RedBean runs in frozen mode it will throw an exception. + * This function returns the primary key ID of the inserted + * bean. + * + * The return value is an integer if possible. If it is not possible to + * represent the value as an integer a string will be returned. We use + * explicit casts instead of functions to preserve performance + * (0.13 vs 0.28 for 10000 iterations on Core i3). + * + * @param OODBBean|SimpleModel $bean bean to store + * + * @return integer|string + * + * @throws Security + */ + public function store( $bean ) + { + $bean = $this->unboxIfNeeded( $bean ); + return $this->repository->store( $bean ); + } + + /** + * Loads a bean from the object database. + * It searches for a OODBBean Bean Object in the + * database. It does not matter how this bean has been stored. + * RedBean uses the primary key ID $id and the string $type + * to find the bean. The $type specifies what kind of bean you + * are looking for; this is the same type as used with the + * dispense() function. If RedBean finds the bean it will return + * the OODB Bean object; if it cannot find the bean + * RedBean will return a new bean of type $type and with + * primary key ID 0. In the latter case it acts basically the + * same as dispense(). + * + * Important note: + * If the bean cannot be found in the database a new bean of + * the specified type will be generated and returned. + * + * @param string $type type of bean you want to load + * @param integer $id ID of the bean you want to load + * + * @throws SQL + * + * @return OODBBean + * + */ + public function load( $type, $id ) + { + return $this->repository->load( $type, $id ); + } + + /** + * Removes a bean from the database. + * This function will remove the specified OODBBean + * Bean Object from the database. + * + * @param OODBBean|SimpleModel $bean bean you want to remove from database + * + * @return void + * + * @throws Security + */ + public function trash( $bean ) + { + $bean = $this->unboxIfNeeded( $bean ); + return $this->repository->trash( $bean ); + } + + /** + * Returns an array of beans. Pass a type and a series of ids and + * this method will bring you the corresponding beans. + * + * important note: Because this method loads beans using the load() + * function (but faster) it will return empty beans with ID 0 for + * every bean that could not be located. The resulting beans will have the + * passed IDs as their keys. + * + * @param string $type type of beans + * @param array $ids ids to load + * + * @return array + */ + public function batch( $type, $ids ) + { + return $this->repository->batch( $type, $ids ); + } + + /** + * This is a convenience method; it converts database rows + * (arrays) into beans. Given a type and a set of rows this method + * will return an array of beans of the specified type loaded with + * the data fields provided by the result set from the database. + * + * @param string $type type of beans you would like to have + * @param array $rows rows from the database result + * + * @return array + */ + public function convertToBeans( $type, $rows ) + { + return $this->repository->convertToBeans( $type, $rows ); + } + + /** + * Counts the number of beans of type $type. + * This method accepts a second argument to modify the count-query. + * A third argument can be used to provide bindings for the SQL snippet. + * + * @param string $type type of bean we are looking for + * @param string $addSQL additional SQL snippet + * @param array $bindings parameters to bind to SQL + * + * @return integer + * + * @throws SQL + */ + public function count( $type, $addSQL = '', $bindings = array() ) + { + return $this->repository->count( $type, $addSQL, $bindings ); + } + + /** + * Trash all beans of a given type. Wipes an entire type of bean. + * + * @param string $type type of bean you wish to delete all instances of + * + * @return boolean + * + * @throws SQL + */ + public function wipe( $type ) + { + return $this->repository->wipe( $type ); + } + /** * Returns an Association Manager for use with OODB. * A simple getter function to obtain a reference to the association manager used for * storage and more. * - * @return RedBean_AssociationManager + * @return AssociationManager * - * @throws RedBean_Exception_Security + * @throws Security */ public function getAssociationManager() { if ( !isset( $this->assocManager ) ) { - throw new RedBean_Exception_Security( 'No association manager available.' ); + throw new RedException( 'No association manager available.' ); } return $this->assocManager; @@ -6389,92 +7611,99 @@ class RedBean_OODB extends RedBean_Observable * A simple setter function to set the association manager to be used for storage and * more. * - * @param RedBean_AssociationManager $assoc sets the association manager to be used + * @param AssociationManager $assoc sets the association manager to be used * * @return void */ - public function setAssociationManager( RedBean_AssociationManager $assocManager ) + public function setAssociationManager( AssociationManager $assocManager ) { $this->assocManager = $assocManager; } /** - * Sets a dependency list. Dependencies can be used to make - * certain beans depend on others. This causes dependent beans to get removed - * once the bean they depend on has been removed as well. - * A dependency takes the form: + * Returns the currently used repository instance. + * For testing purposes only. * - * $me => depends on array( $bean1, $bean2 ) - * - * For instance a to inform RedBeanPHP about the fact that a page - * depends on a book: - * - * 'page' => array('book') - * - * A bean can depend on multiple other beans. - * - * A dependency does two things: - * - * 1. Adds a ON CASCADE DELETE - * 2. trashes the depending bean if the entry in the ownList is removed - * - * @param array $dep - * - * @return void + * @return Repository */ - public function setDepList( $dependencyList ) + public function getCurrentRepository() { - $this->dep = $dependencyList; + return $this->repository; } /** - * Preloads certain properties for beans. - * Understands aliases. + * Binds an SQL function to a column. + * This method can be used to setup a decode/encode scheme or + * perform UUID insertion. This method is especially useful for handling + * MySQL spatial columns, because they need to be processed first using + * the asText/GeomFromText functions. * - * Usage: $redbean->preload($books, array('coauthor'=>'author')); - * - * Usage for nested beans: - * - * $redbean->preload($texts, array('page', 'page.book', 'page.book.author')); - * - * preloads pages, books and authors. - * You may also use a shortcut here: - * - * $redbean->preload($texts, array('page', '*.book', '*.author')); - * - * Can also load preload lists: - * - * $redbean->preload($books, array('ownPage'=>'page', '*.ownText'=>'text', 'sharedTag'=>'tag')); - * - * @param array $beans beans - * @param array $types types to load - * - * @return array + * @param string $mode (read or write) + * @param string $field + * @param string $function */ - public function preload( $beans, $typeList, $closure = NULL ) + public function bindFunc( $mode, $field, $function ) { - $preloader = new RedBean_Preloader( $this ); + list( $type, $property ) = explode( '.', $field ); + $mode = ($mode === 'write') ? QueryWriter::C_SQLFILTER_WRITE : QueryWriter::C_SQLFILTER_READ; - return $preloader->load( $beans, $typeList, $closure ); + if ( !isset( self::$sqlFilters[$mode] ) ) self::$sqlFilters[$mode] = array(); + if ( !isset( self::$sqlFilters[$mode][$type] ) ) self::$sqlFilters[$mode][$type] = array(); + + if ( is_null( $function ) ) { + unset( self::$sqlFilters[$mode][$type][$property] ); + } else { + if ($mode === QueryWriter::C_SQLFILTER_WRITE) { + self::$sqlFilters[$mode][$type][$property] = $function.'(?)'; + } else { + self::$sqlFilters[$mode][$type][$property] = $function."($field)"; + } + } + + AQueryWriter::setSQLFilters( self::$sqlFilters, ( !$this->isFrozen ) ); } } +} +namespace RedBeanPHP { -class RedBean_ToolBox +use RedBeanPHP\OODB as OODB; +use RedBeanPHP\QueryWriter as QueryWriter; +use RedBeanPHP\Adapter\DBAdapter as DBAdapter; +use RedBeanPHP\Adapter as Adapter; + +/** + * @file RedBean/ToolBox.php + * @desc A RedBeanPHP-wide service locator + * @author Gabor de Mooij and the RedBeanPHP community + * @license BSD/GPLv2 + * + * ToolBox. + * The toolbox is an integral part of RedBeanPHP providing the basic + * architectural building blocks to manager objects, helpers and additional tools + * like plugins. A toolbox contains the three core components of RedBeanPHP: + * the adapter, the query writer and the core functionality of RedBeanPHP in + * OODB. + * + * copyright (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community. + * This source file is subject to the BSD/GPLv2 License that is bundled + * with this source code in the file license.txt. + */ +class ToolBox { /** - * @var RedBean_OODB + * @var OODB */ protected $oodb; /** - * @var RedBean_QueryWriter + * @var QueryWriter */ protected $writer; /** - * @var RedBean_Adapter_DBAdapter + * @var DBAdapter */ protected $adapter; @@ -6486,13 +7715,13 @@ class RedBean_ToolBox * the adapter, the query writer and the core functionality of RedBeanPHP in * OODB. * - * @param RedBean_OODB $oodb Object Database - * @param RedBean_Adapter_DBAdapter $adapter Adapter - * @param RedBean_QueryWriter $writer Writer + * @param OODB $oodb Object Database + * @param DBAdapter $adapter Adapter + * @param QueryWriter $writer Writer * - * @return RedBean_ToolBox + * @return ToolBox */ - public function __construct( RedBean_OODB $oodb, RedBean_Adapter $adapter, RedBean_QueryWriter $writer ) + public function __construct( OODB $oodb, Adapter $adapter, QueryWriter $writer ) { $this->oodb = $oodb; $this->adapter = $adapter; @@ -6506,7 +7735,7 @@ class RedBean_ToolBox * The Query Writer is responsible for building the queries for a * specific database and executing them through the adapter. * - * @return RedBean_QueryWriter + * @return QueryWriter */ public function getWriter() { @@ -6519,7 +7748,7 @@ class RedBean_ToolBox * single beans. Other components rely * on OODB for their basic functionality. * - * @return RedBean_OODB + * @return OODB */ public function getRedBean() { @@ -6531,48 +7760,230 @@ class RedBean_ToolBox * The adapter is responsible for executing the query and binding the values. * The adapter also takes care of transaction handling. * - * @return RedBean_Adapter_DBAdapter + * @return DBAdapter */ public function getDatabaseAdapter() { return $this->adapter; } } +} +namespace RedBeanPHP { -class RedBean_AssociationManager extends RedBean_Observable +use RedBeanPHP\ToolBox as ToolBox; +use RedBeanPHP\OODB as OODB; +use RedBeanPHP\RedException\Security as Security; +use RedBeanPHP\OODBBean as OODBBean; + +/** + * RedBean Finder + * + * @file RedBean/Finder.php + * @desc Helper class to harmonize APIs. + * @author Gabor de Mooij and the RedBeanPHP Community + * @license BSD/GPLv2 + * + * copyright (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community + * This source file is subject to the BSD/GPLv2 License that is bundled + * with this source code in the file license.txt. + */ +class Finder { /** - * @var RedBean_OODB + * @var ToolBox + */ + protected $toolbox; + + /** + * @var OODB + */ + protected $redbean; + + /** + * Constructor. + * The Finder requires a toolbox. + * + * @param ToolBox $toolbox + */ + public function __construct( ToolBox $toolbox ) + { + $this->toolbox = $toolbox; + $this->redbean = $toolbox->getRedBean(); + } + + /** + * Finds a bean using a type and a where clause (SQL). + * As with most Query tools in RedBean you can provide values to + * be inserted in the SQL statement by populating the value + * array parameter; you can either use the question mark notation + * or the slot-notation (:keyname). + * + * @param string $type type the type of bean you are looking for + * @param string $sql sql SQL query to find the desired bean, starting right after WHERE clause + * @param array $bindings values array of values to be bound to parameters in query + * + * @return array + * + * @throws Security + */ + public function find( $type, $sql = NULL, $bindings = array() ) + { + if ( !is_array( $bindings ) ) { + throw new RedException( + 'Expected array, ' . gettype( $bindings ) . ' given.' + ); + } + + return $this->redbean->find( $type, array(), $sql, $bindings ); + } + + /** + * @see Finder::find + * The variation also exports the beans (i.e. it returns arrays). + * + * @param string $type type the type of bean you are looking for + * @param string $sql sql SQL query to find the desired bean, starting right after WHERE clause + * @param array $bindings values array of values to be bound to parameters in query + * + * @return array + */ + public function findAndExport( $type, $sql = NULL, $bindings = array() ) + { + $arr = array(); + foreach ( $this->find( $type, $sql, $bindings ) as $key => $item ) { + $arr[] = $item->export(); + } + + return $arr; + } + + /** + * @see Finder::find + * This variation returns the first bean only. + * + * @param string $type type the type of bean you are looking for + * @param string $sql sql SQL query to find the desired bean, starting right after WHERE clause + * @param array $bindings values array of values to be bound to parameters in query + * + * @return OODBBean + */ + public function findOne( $type, $sql = NULL, $bindings = array() ) + { + $sql = $this->toolbox->getWriter()->glueLimitOne( $sql ); + + $items = $this->find( $type, $sql, $bindings ); + + if ( empty($items) ) { + return NULL; + } + + return reset( $items ); + } + + /** + * @see Finder::find + * This variation returns the last bean only. + * + * @param string $type the type of bean you are looking for + * @param string $sql SQL query to find the desired bean, starting right after WHERE clause + * @param array $bindings values array of values to be bound to parameters in query + * + * @return OODBBean + */ + public function findLast( $type, $sql = NULL, $bindings = array() ) + { + $items = $this->find( $type, $sql, $bindings ); + + if ( empty($items) ) { + return NULL; + } + + return end( $items ); + } + + /** + * @see Finder::find + * Convience method. Tries to find beans of a certain type, + * if no beans are found, it dispenses a bean of that type. + * + * @param string $type the type of bean you are looking for + * @param string $sql SQL query to find the desired bean, starting right after WHERE clause + * @param array $bindings values array of values to be bound to parameters in query + * + * @return array + */ + public function findOrDispense( $type, $sql = NULL, $bindings = array() ) + { + $foundBeans = $this->find( $type, $sql, $bindings ); + + if ( empty( $foundBeans ) ) { + return array( $this->redbean->dispense( $type ) ); + } else { + return $foundBeans; + } + } +} +} + +namespace RedBeanPHP { + +use RedBeanPHP\Observable as Observable; +use RedBeanPHP\OODB as OODB; +use RedBeanPHP\Adapter\DBAdapter as DBAdapter; +use RedBeanPHP\QueryWriter as QueryWriter; +use RedBeanPHP\OODBBean as OODBBean; +use RedBeanPHP\RedException as RedException; +use RedBeanPHP\RedException\Security as Security; +use RedBeanPHP\RedException\SQL as SQL; +use RedBeanPHP\ToolBox as ToolBox; + +/** + * Association Manager + * + * @file RedBean/AssociationManager.php + * @desc Manages simple bean associations. + * @author Gabor de Mooij and the RedBeanPHP Community + * @license BSD/GPLv2 + * + * (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community. + * This source file is subject to the BSD/GPLv2 License that is bundled + * with this source code in the file license.txt. + */ +class AssociationManager extends Observable +{ + + /** + * @var OODB */ protected $oodb; /** - * @var RedBean_Adapter_DBAdapter + * @var DBAdapter */ protected $adapter; /** - * @var RedBean_QueryWriter + * @var QueryWriter */ protected $writer; /** - * Handles Exceptions. Suppresses exceptions caused by missing structures. + * Handles\Exceptions. Suppresses exceptions caused by missing structures. * - * @param Exception $exception + * @param\Exception $exception * * @return void * - * @throws Exception + * @throws\Exception */ - private function handleException( Exception $exception ) + private function handleException(\Exception $exception ) { - if ( !$this->writer->sqlStateIn( $exception->getSQLState(), + if ( $this->oodb->isFrozen() || !$this->writer->sqlStateIn( $exception->getSQLState(), array( - RedBean_QueryWriter::C_SQLSTATE_NO_SUCH_TABLE, - RedBean_QueryWriter::C_SQLSTATE_NO_SUCH_COLUMN ) + QueryWriter::C_SQLSTATE_NO_SUCH_TABLE, + QueryWriter::C_SQLSTATE_NO_SUCH_COLUMN ) ) ) { throw $exception; @@ -6584,51 +7995,24 @@ class RedBean_AssociationManager extends RedBean_Observable * Returns the many-to-many related rows of table $type for bean $bean using additional SQL in $sql and * $bindings bindings. If $getLinks is TRUE, link rows are returned instead. * - * @param RedBean_OODBBean $bean reference bean + * @param OODBBean $bean reference bean * @param string $type target type - * @param boolean $getLinks TRUE returns rows from the link table * @param string $sql additional SQL snippet * @param array $bindings bindings * * @return array * - * @throws RedBean_Exception_Security - * @throws RedBean_Exception_SQL + * @throws Security + * @throws SQL */ - private function relatedRows( $bean, $type, $getLinks = FALSE, $sql = '', $bindings = array() ) + private function relatedRows( $bean, $type, $sql = '', $bindings = array() ) { - if ( !is_array( $bean ) && !( $bean instanceof RedBean_OODBBean ) ) { - throw new RedBean_Exception_Security( - 'Expected array or RedBean_OODBBean but got:' . gettype( $bean ) - ); - } - - $ids = array(); - if ( is_array( $bean ) ) { - $beans = $bean; - foreach ( $beans as $singleBean ) { - if ( !( $singleBean instanceof RedBean_OODBBean ) ) { - throw new RedBean_Exception_Security( - 'Expected RedBean_OODBBean in array but got:' . gettype( $singleBean ) - ); - } - $ids[] = $singleBean->id; - } - $bean = reset( $beans ); - } else { - $ids[] = $bean->id; - } - + $ids = array( $bean->id ); $sourceType = $bean->getMeta( 'type' ); try { - if ( !$getLinks ) { - return $this->writer->queryRecordRelated( $sourceType, $type, $ids, $sql, $bindings ); - } else { - return $this->writer->queryRecordLinks( $sourceType, $type, $ids, $sql, $bindings ); - } - } catch ( RedBean_Exception_SQL $exception ) { + return $this->writer->queryRecordRelated( $sourceType, $type, $ids, $sql, $bindings ); + } catch ( SQL $exception ) { $this->handleException( $exception ); - return array(); } } @@ -6639,32 +8023,46 @@ class RedBean_AssociationManager extends RedBean_Observable * This method is used by associate. This method also accepts a base bean to be used * as the template for the link record in the database. * - * @param RedBean_OODBBean $bean1 first bean - * @param RedBean_OODBBean $bean2 second bean - * @param RedBean_OODBBean $bean base bean (association record) + * @param OODBBean $bean1 first bean + * @param OODBBean $bean2 second bean + * @param OODBBean $bean base bean (association record) * - * @throws Exception|RedBean_Exception_SQL + * @throws\Exception|SQL * * @return mixed */ - protected function associateBeans( RedBean_OODBBean $bean1, RedBean_OODBBean $bean2, RedBean_OODBBean $bean ) + protected function associateBeans( OODBBean $bean1, OODBBean $bean2, OODBBean $bean ) { - $property1 = $bean1->getMeta( 'type' ) . '_id'; $property2 = $bean2->getMeta( 'type' ) . '_id'; if ( $property1 == $property2 ) { $property2 = $bean2->getMeta( 'type' ) . '2_id'; } + + if ( !$this->oodb->isFrozen() ) { + //Dont mess with other tables, only add the unique constraint if: + //1. the table exists (otherwise we cant inspect it) + //2. the table only contains N-M fields: ID, N-ID, M-ID. + $unique = array( $property1, $property2 ); + $type = $bean->getMeta( 'type' ); + $tables = $this->writer->getTables(); + if ( in_array( $type, $tables ) && !$this->oodb->isChilled( $type ) ) { + $columns = ( $this->writer->getColumns( $type ) ); + if ( count( $columns ) === 3 + && isset( $columns[ 'id' ] ) + && isset( $columns[ $property1 ] ) + && isset( $columns[ $property2 ] ) ) { + $bean->setMeta( 'buildcommand.unique', array( $unique ) ); + } + } - //add a build command for Unique Indexes - $bean->setMeta( 'buildcommand.unique', array( array( $property1, $property2 ) ) ); + //add a build command for Single Column Index (to improve performance in case unqiue cant be used) + $indexName1 = 'index_for_' . $bean->getMeta( 'type' ) . '_' . $property1; + $indexName2 = 'index_for_' . $bean->getMeta( 'type' ) . '_' . $property2; - //add a build command for Single Column Index (to improve performance in case unqiue cant be used) - $indexName1 = 'index_for_' . $bean->getMeta( 'type' ) . '_' . $property1; - $indexName2 = 'index_for_' . $bean->getMeta( 'type' ) . '_' . $property2; - - $bean->setMeta( 'buildcommand.indexes', array( $property1 => $indexName1, $property2 => $indexName2 ) ); + $bean->setMeta( 'buildcommand.indexes', array( $property1 => $indexName1, $property2 => $indexName2 ) ); + } $this->oodb->store( $bean1 ); $this->oodb->store( $bean2 ); @@ -6689,9 +8087,9 @@ class RedBean_AssociationManager extends RedBean_Observable } } $results[] = $id; - } catch ( RedBean_Exception_SQL $exception ) { + } catch ( SQL $exception ) { if ( !$this->writer->sqlStateIn( $exception->getSQLState(), - array( RedBean_QueryWriter::C_SQLSTATE_INTEGRITY_CONSTRAINT_VIOLATION ) ) + array( QueryWriter::C_SQLSTATE_INTEGRITY_CONSTRAINT_VIOLATION ) ) ) { throw $exception; } @@ -6703,9 +8101,9 @@ class RedBean_AssociationManager extends RedBean_Observable /** * Constructor * - * @param RedBean_ToolBox $tools toolbox + * @param ToolBox $tools toolbox */ - public function __construct( RedBean_ToolBox $tools ) + public function __construct( ToolBox $tools ) { $this->oodb = $tools->getRedBean(); $this->adapter = $tools->getDatabaseAdapter(); @@ -6735,8 +8133,8 @@ class RedBean_AssociationManager extends RedBean_Observable * two in a link table. Instead of two single beans this method also accepts * two sets of beans. Returns the ID or the IDs of the linking beans. * - * @param RedBean_OODBBean|array $beans1 one or more beans to form the association - * @param RedBean_OODBBean|array $beans2 one or more beans to form the association + * @param OODBBean|array $beans1 one or more beans to form the association + * @param OODBBean|array $beans2 one or more beans to form the association * * @return array */ @@ -6768,20 +8166,20 @@ class RedBean_AssociationManager extends RedBean_Observable * with reference bean(s) $bean. The query can be tuned using an * SQL snippet for additional filtering. * - * @param RedBean_OODBBean|array $bean a bean object or an array of beans + * @param OODBBean|array $bean a bean object or an array of beans * @param string $type type of bean you're interested in * @param string $sql SQL snippet (optional) * @param array $bindings bindings for your SQL string * * @return integer * - * @throws RedBean_Exception_Security + * @throws Security */ public function relatedCount( $bean, $type, $sql = NULL, $bindings = array() ) { - if ( !( $bean instanceof RedBean_OODBBean ) ) { - throw new RedBean_Exception_Security( - 'Expected array or RedBean_OODBBean but got:' . gettype( $bean ) + if ( !( $bean instanceof OODBBean ) ) { + throw new RedException( + 'Expected array or OODBBean but got:' . gettype( $bean ) ); } @@ -6793,48 +8191,13 @@ class RedBean_AssociationManager extends RedBean_Observable try { return $this->writer->queryRecordCountRelated( $beanType, $type, $bean->id, $sql, $bindings ); - } catch ( RedBean_Exception_SQL $exception ) { + } catch ( SQL $exception ) { $this->handleException( $exception ); return 0; } } - /** - * Returns all ids of beans of type $type that are related to $bean. If the - * $getLinks parameter is set to boolean TRUE this method will return the ids - * of the association beans instead. You can also add additional SQL. This SQL - * will be appended to the original query string used by this method. Note that this - * method will not return beans, just keys. For a more convenient method see the R-facade - * method related(), that is in fact a wrapper for this method that offers a more - * convenient solution. If you want to make use of this method, consider the - * OODB batch() method to convert the ids to beans. - * - * Since 3.2, you can now also pass an array of beans instead just one - * bean as the first parameter. - * - * @throws RedBean_Exception_SQL - * - * @param RedBean_OODBBean|array $bean reference bean - * @param string $type target type - * @param boolean $getLinks whether you are interested in the assoc records - * @param string $sql room for additional SQL - * @param array $bindings bindings for SQL snippet - * - * @return array - */ - public function related( $bean, $type, $getLinks = FALSE, $sql = '', $bindings = array() ) - { - $sql = $this->writer->glueSQLCondition( $sql ); - - $rows = $this->relatedRows( $bean, $type, $getLinks, $sql, $bindings ); - - $ids = array(); - foreach ( $rows as $row ) $ids[] = $row['id']; - - return $ids; - } - /** * Breaks the association between two beans. This method unassociates two beans. If the * method succeeds the beans will no longer form an association. In the database @@ -6844,8 +8207,8 @@ class RedBean_AssociationManager extends RedBean_Observable * set to boolean TRUE this method will remove the beans without their consent, * bypassing FUSE. This can be used to improve performance. * - * @param RedBean_OODBBean $bean1 first bean - * @param RedBean_OODBBean $bean2 second bean + * @param OODBBean $bean1 first bean + * @param OODBBean $bean2 second bean * @param boolean $fast If TRUE, removes the entries by query without FUSE * * @return void @@ -6879,7 +8242,7 @@ class RedBean_AssociationManager extends RedBean_Observable $bean = reset( $beans ); $this->oodb->trash( $bean ); } - } catch ( RedBean_Exception_SQL $exception ) { + } catch ( SQL $exception ) { $this->handleException( $exception ); } } @@ -6893,69 +8256,21 @@ class RedBean_AssociationManager extends RedBean_Observable * models about this. If you want to notify FUSE models about deletion use a foreach-loop * with unassociate() instead. (that might be slower though) * - * @param RedBean_OODBBean $bean reference bean + * @param OODBBean $bean reference bean * @param string $type type of beans that need to be unassociated * * @return void */ - public function clearRelations( RedBean_OODBBean $bean, $type ) + public function clearRelations( OODBBean $bean, $type ) { $this->oodb->store( $bean ); try { $this->writer->deleteRelations( $bean->getMeta( 'type' ), $type, $bean->id ); - } catch ( RedBean_Exception_SQL $exception ) { + } catch ( SQL $exception ) { $this->handleException( $exception ); } } - /** - * Given two beans this function returns TRUE if they are associated using a - * many-to-many association, FALSE otherwise. - * - * @throws RedBean_Exception_SQL - * - * @param RedBean_OODBBean $bean1 bean - * @param RedBean_OODBBean $bean2 bean - * - * @return boolean - */ - public function areRelated( RedBean_OODBBean $bean1, RedBean_OODBBean $bean2 ) - { - try { - $row = $this->writer->queryRecordLink( $bean1->getMeta( 'type' ), $bean2->getMeta( 'type' ), $bean1->id, $bean2->id ); - - return (boolean) $row; - } catch ( RedBean_Exception_SQL $exception ) { - $this->handleException( $exception ); - - return FALSE; - } - } - - /** - * Swaps a property of two beans. - * Given two beans and a property this method swaps the value of the - * property in the beans. - * - * @deprecated - * This method does not seem very useful. - * - * @param array $beans beans - * @param string $property property to swap - * - * @return void - */ - public function swap( $beans, $property ) - { - $bean1 = array_shift( $beans ); - $bean2 = array_shift( $beans ); - $tmp = $bean1->$property; - $bean1->$property = $bean2->$property; - $bean2->$property = $tmp; - $this->oodb->store( $bean1 ); - $this->oodb->store( $bean2 ); - } - /** * Returns all the beans associated with $bean. * This method will return an array containing all the beans that have @@ -6971,7 +8286,7 @@ class RedBean_AssociationManager extends RedBean_Observable * Since 3.2, you can now also pass an array of beans instead just one * bean as the first parameter. * - * @param RedBean_OODBBean|array $bean the bean you have + * @param OODBBean|array $bean the bean you have * @param string $type the type of beans you want * @param string $sql SQL snippet for extra filtering * @param array $bindings values to be inserted in SQL slots @@ -6979,11 +8294,11 @@ class RedBean_AssociationManager extends RedBean_Observable * * @return array */ - public function relatedSimple( $bean, $type, $sql = '', $bindings = array() ) + public function related( $bean, $type, $sql = '', $bindings = array() ) { $sql = $this->writer->glueSQLCondition( $sql ); - $rows = $this->relatedRows( $bean, $type, FALSE, $sql, $bindings ); + $rows = $this->relatedRows( $bean, $type, $sql, $bindings ); $links = array(); foreach ( $rows as $key => $row ) { @@ -7004,826 +8319,30 @@ class RedBean_AssociationManager extends RedBean_Observable return $beans; } - - /** - * Returns only a single associated bean. - * You can also pass some extra SQL and - * values for that SQL to filter your results after fetching the - * related beans. - * - * @see RedBean_AssociationManager::relatedSimple. - * - * @param RedBean_OODBBean $bean bean provided - * @param string $type type of bean you are searching for - * @param string $sql SQL for extra filtering - * @param array $bindings values to be inserted in SQL slots - * - * @return RedBean_OODBBean - */ - public function relatedOne( RedBean_OODBBean $bean, $type, $sql = NULL, $bindings = array() ) - { - $beans = $this->relatedSimple( $bean, $type, $sql, $bindings ); - - if ( empty( $beans ) ) { - return NULL; - } - - return reset( $beans ); - } - - /** - * Returns only the last, single associated bean. - * You can also pass some extra SQL and - * values for that SQL to filter your results after fetching the - * related beans. - * - * @see RedBean_AssociationManager::relatedSimple. - * - * @param RedBean_OODBBean $bean bean provided - * @param string $type type of bean you are searching for - * @param string $sql SQL for extra filtering - * @param array $bindings values to be inserted in SQL slots - * - * @return RedBean_OODBBean - */ - public function relatedLast( RedBean_OODBBean $bean, $type, $sql = NULL, $bindings = array() ) - { - $beans = $this->relatedSimple( $bean, $type, $sql, $bindings ); - - if ( empty( $beans ) ) { - return NULL; - } - - return end( $beans ); - } - +} } - -class RedBean_Preloader -{ - - /** - * @var RedBean_AssociationManager - */ - private $assocManager; - - /** - * @var RedBean_OODB - */ - private $oodb; - - /** - * @var integer - */ - private $counterID = 0; - - /** - * @var array - */ - private $filteredBeans = array(); - - /** - * @var array - */ - private $retrievals = array(); - - /** - * @var integer - */ - private $iterationIndex = 0; - - /** - * Extracts the type list for preloader. - * Explodes a list of comma separated types and splits - * the type definition in list -> type pairs if needed. - * - * @param array|string $typeList list of types - * - * @return array - */ - private function extractTypesFromTypeList( $typeList ) - { - if ( !is_string( $typeList ) ) { - return $typeList; - } - - $typeList = explode( ',', $typeList ); - - $types = array(); - foreach ( $typeList as $value ) { - if ( strpos( $value, '|' ) !== FALSE ) { - list( $key, $newValue ) = explode( '|', $value ); - - $types[$key] = $newValue; - } else { - $types[] = $value; - } - } - - return $types; - } - - /** - * Extracts preloading request for type from array. - * - * @param array $typeInfo type info - * - * @return array - */ - private function extractTypeInfo( $typeInfo ) - { - if ( is_array( $typeInfo ) && !isset( $typeInfo[1] ) ) { - $typeInfo[1] = array( NULL, array() ); - } - - list( $type, $sqlObj ) = ( is_array( $typeInfo ) ? $typeInfo : array( $typeInfo, array( NULL, array() ) ) ); - - if ( !isset($sqlObj[1]) ) { - $sqlObj[1] = array(); - } - - list( $sql, $bindings ) = $sqlObj; - - return array( $type, $sql, $bindings ); - } - - /** - * Marks the input beans. - * This method is used to connect the current selection of beans to - * input beans. - * - * @param array $beans beans to connect to input beans - * - * @return array - */ - private function markBeans( $filteredBeans ) - { - $this->counterID = 0; - - foreach ( $filteredBeans as $bean ) { - $bean->setMeta( 'sys.input-bean-id', array( $this->counterID => $this->counterID ) ); - $this->counterID++; - } - - return $filteredBeans; - } - - /** - * Adds the beans from the next step in the path to the collection of filtered - * beans. - * - * @param string $nesting property (list or bean property) - * - * @return void - */ - private function addBeansForNextStepInPath( $nesting ) - { - $filtered = array(); - foreach ( $this->filteredBeans as $bean ) { - $addInputIDs = $bean->getMeta( 'sys.input-bean-id' ); - - if ( isset($bean->$nesting) && is_array( $bean->$nesting ) ) { - $nestedBeans = $bean->$nesting; - - foreach ( $nestedBeans as $nestedBean ) { - $this->addInputBeanIDsToBean( $nestedBean, $addInputIDs ); - } - - $filtered = array_merge( $filtered, $nestedBeans ); - } elseif ( isset($bean->$nesting) && !is_null( $bean->$nesting ) ) { - $this->addInputBeanIDsToBean( $bean->$nesting, $addInputIDs ); - $filtered[] = $bean->$nesting; - } - } - - $this->filteredBeans = $filtered; - } - - /** - * Expands * and & symbols in preload syntax. - * Also adds the returned field name to the list of fields. - * - * @param string $key key value for this field - * @param string $type type of bean - * @param string $oldField last field we've processed - * @param array $oldFields list of previously gathered field names - * - * @return string - */ - private function getPreloadField( $key, $type, $oldField, &$oldFields ) - { - $field = ( is_numeric( $key ) ) ? $type : $key; //use an alias? - - if ( strpos( $field, '*' ) !== FALSE ) { - $oldFields[] = $oldField; - $field = str_replace( '*', implode( '.', $oldFields ), $field ); - } - - if ( strpos( $field, '&' ) !== FALSE ) { - $field = str_replace( '&', implode( '.', $oldFields ), $field ); - } - - return $field; - } - - /** - * For Preloader: adds the IDs of your input beans to the nested beans, otherwise - * we dont know how to pass them to the each-function later on. - * - * @param RedBean_OODBBean $nestedBean nested bean - * @param array $addInputIDs input ids - * - * @return void - */ - private function addInputBeanIDsToBean( $nestedBean, $addInputIDs ) - { - $currentInputBeanIDs = $nestedBean->getMeta( 'sys.input-bean-id' ); - - if ( !is_array( $currentInputBeanIDs ) ) { - $currentInputBeanIDs = array(); - } - - foreach ( $addInputIDs as $addInputID ) { - $currentInputBeanIDs[$addInputID] = $addInputID; - } - - $nestedBean->setMeta( 'sys.input-bean-id', $currentInputBeanIDs ); - } - - /** - * For preloader: calls the function defined in $closure with retrievals for each - * bean in the first parameter. - * - * @param closure|string $closure closure to invoke per bean - * @param array $beans beans to iterate over - * @param array $retrievals retrievals to send as arguments to closure - * - * @return void - */ - private function invokePreloadEachFunction( $closure, $beans, $retrievals ) - { - if ( $closure ) { - $key = 0; - - foreach ( $beans as $bean ) { - $bindings = array(); - - foreach ( $retrievals as $r ) { - $bindings[] = ( isset( $r[$key] ) ) ? $r[$key] : NULL; - } - - array_unshift( $bindings, $bean ); - - call_user_func_array( $closure, $bindings ); - - $key++; - } - } - } - - /** - * Fills the retrieval array with the beans in the (recently) retrieved - * shared/own list. This method asks the $filteredBean which original input bean - * it belongs to, then it will fill the parameter array for the specified - * iteration with the beans obtained for the filtered bean. This ensures the - * callback function for ::each will receive the correct bean lists as - * parameters for every iteration. - * - * @param RedBean_OODBBean $filteredBean the bean we've retrieved lists for - * @param array $list the list we've retrieved for the bean - * - * @return void - */ - private function fillParamArrayRetrievals( $filteredBean, $list ) - { - $inputBeanIDs = $filteredBean->getMeta( 'sys.input-bean-id' ); - - foreach ( $inputBeanIDs as $inputBeanID ) { - if ( !isset( $this->retrievals[$this->iterationIndex][$inputBeanID] ) ) { - $this->retrievals[$this->iterationIndex][$inputBeanID] = array(); - } - - foreach ( $list as $listKey => $listBean ) { - $this->retrievals[$this->iterationIndex][$inputBeanID][$listKey] = $listBean; - } - } - } - - /** - * Fills retrieval array with parent beans. - * - * @param array $inputBeanIDs ids - * @param RedBean_OODBBean $parent parent bean - */ - private function fillParamArrayRetrievalsWithParent( $inputBeanIDs, $parent ) - { - foreach ( $inputBeanIDs as $inputBeanID ) { - $this->retrievals[$this->iterationIndex][$inputBeanID] = $parent; - } - } - - /** - * Gathers the IDs to preload and maps the ids to the original beans. - * - * @param array $filteredBeans filtered beans - * @param string $field field name - * - * @return array - */ - private function gatherIDsToPreloadAndMap( $filteredBeans, $field ) - { - $ids = $map = array(); - - if ( strpos( $field, 'shared' ) !== 0 ) { - // Gather ids to load the desired bean collections - foreach ( $filteredBeans as $bean ) { - - if ( strpos( $field, 'own' ) === 0 ) { - // Based on bean->id for ownlist - $id = $bean->id; - $ids[$id] = $id; - } elseif ( $id = $bean->{$field . '_id'} ) { - // Based on bean_id for parent - $ids[$id] = $id; - - if ( !isset( $map[$id] ) ) { - $map[$id] = array(); - } - - $map[$id][] = $bean; - } - } - } - - return array( $ids, $map ); - } - - /** - * Gathers the own list for a bean from a pool of child beans loaded by - * the preloader. - * - * @param RedBean_OODBBean $filteredBean - * @param array $children - * @param string $link - * - * @return array - */ - private function gatherOwnBeansFromPool( $filteredBean, $children, $link ) - { - $list = array(); - foreach ( $children as $child ) { - if ( $child->$link == $filteredBean->id ) { - $list[$child->id] = $child; - } - } - - return $list; - } - - /** - * Gathers the shared list for a bean from a pool of shared beans loaded - * by the preloader. - * - * @param RedBean_OODBBean $filteredBean - * @param array $sharedBeans - * - * @return array - */ - private function gatherSharedBeansFromPool( $filteredBean, $sharedBeans ) - { - $list = array(); - foreach ( $sharedBeans as $sharedBean ) { - if ( in_array( $filteredBean->id, $sharedBean->getMeta( 'sys.belongs-to' ) ) ) { - $list[] = $sharedBean; - } - } - - return $list; - } - - /** - * Initializes the preloader. - * Initializes the filtered beans array, the retrievals array and - * the iteration index. - */ - private function init() - { - $this->iterationIndex = 0; - $this->retrievals = array(); - $this->filteredBeans = array(); - } - - /** - * Preloads the shared beans. - * - * @param string $type type of beans to load - * @param string $sql additional SQL snippet for loading - * @param array $bindings parameter bindings for SQL snippet - * @param string $field field to store preloaded beans in - * - * @return void - */ - private function preloadSharedBeans( $type, $sql, $bindings, $field ) - { - $sharedBeans = $this->assocManager->relatedSimple( $this->filteredBeans, $type, $sql, $bindings ); - - // Let the filtered beans gather their beans - foreach ( $this->filteredBeans as $filteredBean ) { - $list = $this->gatherSharedBeansFromPool( $filteredBean, $sharedBeans ); - - $filteredBean->setProperty( $field, $list, TRUE, TRUE ); - - $this->fillParamArrayRetrievals( $filteredBean, $list ); - } - } - - /** - * Preloads the own beans. - * - * @param string $type type of beans to load - * @param string $sql additional SQL snippet for loading - * @param array $bindings parameter bindings for SQL snippet - * @param string $field field to store preloaded beans in - * @param array $ids list of ids to load - * @param string $alias alias for own list (optional) - * - * @return void - */ - private function preloadOwnBeans( $type, $sql, $bindings, $field, $ids, $alias = null ) - { - $bean = reset( $this->filteredBeans ); - $link = ( $alias === NULL ) ? $bean->getMeta( 'type' ) . '_id' : ( $alias.'_id' ); - - $children = $this->oodb->find( $type, array( $link => $ids ), $sql, $bindings ); - - foreach ( $this->filteredBeans as $filteredBean ) { - $list = $this->gatherOwnBeansFromPool( $filteredBean, $children, $link ); - - $filteredBean->setProperty( $field, $list, TRUE, TRUE ); - - $this->fillParamArrayRetrievals( $filteredBean, $list ); - } - } - - /** - * Preloads parent beans. - * - * @param string $type type of bean to load - * @param string $field field to store parent in - * @param array $ids list of ids to load - * @param array $map mapping to use (children indexed by parent bean ids) - * @param string $sql optional SQL snippet for additional filtering - * @param array $array optional bindings for SQL snippet - * - * @return void - */ - private function preloadParentBeans( $type, $field, $ids, $map, $sql = NULL, $bindings = array() ) - { - foreach ( $this->oodb->find( $type, array( 'id' => $ids ), $sql, $bindings ) as $parent ) { - if (isset($map[$parent->id])) { - foreach ( $map[$parent->id] as $childBean ) { - $childBean->setProperty( $field, $parent ); - - $inputBeanIDs = $childBean->getMeta( 'sys.input-bean-id' ); - - $this->fillParamArrayRetrievalsWithParent( $inputBeanIDs, $parent ); - } - } - } - } - - /** - * Simple input correction function. Checks whether input is a single bean - * and wraps it in an array if necessary. - * - * @param RedBean_OODBBean|array $beanOrBeans input - * - * @return array - */ - private function convertBeanToArrayIfNeeded( $beanOrBeans ) - { - if ( !is_array( $beanOrBeans ) ) { - $beanOrBeans = array( $beanOrBeans ); - } - - return $beanOrBeans; - } - - /** - * Constructor. - * - * @param RedBean_OODB $oodb - */ - public function __construct( $oodb ) - { - $this->oodb = $oodb; - - $this->assocManager = $oodb->getAssociationManager(); - } - - /** - * Preloads certain properties for beans. - * Understands aliases. - * - * Usage: - * - * R::preload($books, 'author'); - * - * - preloads all the authors of all books, - * saves you a query per for-each iteration - * - * R::preload($books, array('coauthor'=>'author')); - * - * - same but with alias - * - * R::preload($texts,'page,page.book,page.book.author'); - * - * - preloads all pages for the texts, the books and the authors - * - * R::preload($texts,'page,*.book,*.author'); - * - * - same as above bit with short syntax (* means prefix with previous types) - * - * R::preload($p,'book,*.author,&.shelf'); - * - * - if author and shelf are on the same level use & instead of *. - * - * The other way around is possible as well, to load child beans in own-lists or - * shared-lists use: - * - * R::preload($books,'ownPage|page,sharedGenre|genre'); - * - * @param array $beans beans beans to use as a reference for preloading - * @param array|string $types types to load, either string or array - * - * @return array - */ - public function load( $beans, $typeList, $closure = NULL ) - { - $beans = $this->convertBeanToArrayIfNeeded( $beans ); - - $this->init(); - - $types = $this->extractTypesFromTypeList( $typeList ); - - $oldFields = array(); - - $oldField = ''; - - foreach ( $types as $key => $typeInfo ) { - list( $type, $sql, $bindings ) = $this->extractTypeInfo( $typeInfo ); - - $this->retrievals[$this->iterationIndex] = array(); - - $alias = NULL; - if ( strpos( $key, '/' ) !== false ) { - list( $key, $alias ) = explode( '/', $key ); - } - - $field = $this->getPreloadField( $key, $type, $oldField, $oldFields ); - - $this->filteredBeans = $this->markBeans( $beans ); - - // Filtering: find the right beans in the path - while ( $p = strpos( $field, '.' ) ) { - $this->addBeansForNextStepInPath( substr( $field, 0, $p ) ); - - $field = substr( $field, $p + 1 ); - } - - $oldField = $field; - - $type = ( strpos( $type, '.' ) !== FALSE ) ? $field : $type; - - if ( count( $this->filteredBeans ) === 0 ) continue; - - list( $ids, $map ) = $this->gatherIDsToPreloadAndMap( $this->filteredBeans, $field ); - - if ( strpos( $field, 'shared' ) === 0 ) { - $this->preloadSharedBeans( $type, $sql, $bindings, $field ); - } elseif ( strpos( $field, 'own' ) === 0 ) { - // Preload for own-list using find - $this->preloadOwnBeans( $type, $sql, $bindings, $field, $ids, $alias ); - } else { - // Preload for parent objects using batch() - $this->preloadParentBeans( $type, $field, $ids, $map, $sql, $bindings ); - } - - $this->iterationIndex++; - } - - $this->invokePreloadEachFunction( $closure, $beans, $this->retrievals ); - } -} - - -class RedBean_AssociationManager_ExtAssociationManager extends RedBean_AssociationManager -{ - - /** - * @deprecated - * - * Associates two beans and allows you to specify a base bean to form the - * link between the two. - * - * This method has been deprecated, please use $bean->link instead to form - * many-to-many associations with additional properties. - * - * @param RedBean_OODBBean $bean1 the first bean you want to associate - * @param RedBean_OODBBean $bean2 the second bean you want to associate - * @param RedBean_OODBBean $baseBean the link bean - * - * @return array - */ - public function extAssociate( RedBean_OODBBean $bean1, RedBean_OODBBean $bean2, RedBean_OODBBean $baseBean ) - { - $table = $this->getTable( array( $bean1->getMeta( 'type' ), $bean2->getMeta( 'type' ) ) ); - - $baseBean->setMeta( 'type', $table ); - - return $this->associateBeans( $bean1, $bean2, $baseBean ); - } - - /** - * @deprecated - * - * Simplified version of extAssociate(). - * Associates two beans $bean1 and $bean2 with additional properties defined in - * third parameter $extra. This third parameter can be either an array, a - * JSON string, a single value (will be assigned to property 'extra') or a - * bean. - * - * This method has been deprecated, please use $bean->link instead to form - * many-to-many associations with additional properties. - * - * @param RedBean_OODBBean $bean1 the first bean you want to associate - * @param RedBean_OODBBean $bean2 the second bean you want to associate - * @param mixed $extra one or more additional properties and values - * - * @return array - */ - public function extAssociateSimple( $beans1, $beans2, $extra = NULL ) - { - if ( !is_array( $extra ) ) { - $info = json_decode( $extra, TRUE ); - - if ( !$info ) $info = array( 'extra' => $extra ); - } else { - $info = $extra; - } - - $bean = $this->oodb->dispense( 'xtypeless' ); - $bean->import( $info ); - - return $this->extAssociate( $beans1, $beans2, $bean ); - } -} - - -class RedBean_Setup -{ - - /** - * This method checks the DSN string. - * Checks the validity of the DSN string. - * If the DSN contains an invalid database identifier this method - * will trigger an error. - * - * @param string $dsn - * - * @return boolean - */ - private static function checkDSN( $dsn ) - { - if ( !preg_match( '/^(mysql|sqlite|pgsql|cubrid|oracle|sqlsrv):/', strtolower( trim( $dsn ) ) ) ) { - trigger_error( 'Unsupported DSN' ); - } - - return TRUE; - } - - /** - * Initializes the database and prepares a toolbox. - * The kickstart method assembles a toolbox based on your DSN and - * credentials and returns it. - * The toolbox contains all the necessary core components for - * RedBeanPHP to start working with your database. Most RedBeanPHP - * components are stand-alone and require a toolbox to work. - * - * @param string|PDO $dsn Database Connection String (or PDO instance) - * @param string $username Username for database - * @param string $password Password for database - * @param boolean $frozen Start in frozen mode? - * - * @return RedBean_ToolBox - */ - public static function kickstart( $dsn, $username = NULL, $password = NULL, $frozen = FALSE, $autoSetEncoding = TRUE ) - { - if ( $dsn instanceof PDO ) { - $db = new RedBean_Driver_PDO( $dsn ); - $dsn = $db->getDatabaseType(); - } else { - self::checkDSN( $dsn ); - - if ( strpos( $dsn, 'oracle' ) === 0 ) { - $db = new RedBean_Driver_OCI( $dsn, $username, $password); - } else { - $db = new RedBean_Driver_PDO( $dsn, $username, $password, $autoSetEncoding ); - } - } - - $adapter = new RedBean_Adapter_DBAdapter( $db ); - - if ( strpos( $dsn, 'pgsql' ) === 0 ) { - $writer = new RedBean_QueryWriter_PostgreSQL( $adapter ); - } else if ( strpos( $dsn, 'sqlite' ) === 0 ) { - $writer = new RedBean_QueryWriter_SQLiteT( $adapter ); - } else if ( strpos( $dsn, 'cubrid' ) === 0 ) { - $writer = new RedBean_QueryWriter_CUBRID( $adapter ); - } else if ( strpos( $dsn, 'oracle' ) === 0 ) { - $writer = new RedBean_QueryWriter_Oracle( $adapter ); - } else if ( strpos( $dsn, 'sqlsrv' ) === 0 ) { - $writer = new RedBean_QueryWriter_SQLServer( $adapter ); - } else { - $writer = new RedBean_QueryWriter_MySQL( $adapter ); - } - - $redbean = new RedBean_OODB( $writer ); - - if ( $frozen ) { - $redbean->freeze( TRUE ); - } - - $toolbox = new RedBean_ToolBox( $redbean, $adapter, $writer ); - - return $toolbox; - } -} - - -interface RedBean_IModelFormatter -{ - /** - * ModelHelper will call this method of the class - * you provide to discover the model - * - * @param string $model - * - * @return string $formattedModel - */ - public function formatModel( $model ); -} - - -interface RedBean_Logger -{ - - /** - * A logger (for PDO or OCI driver) needs to implement the log method. - * The log method will receive logging data. Note that the number of parameters is 0, this means - * all parameters are optional and the number may vary. This way the logger can be used in a very - * flexible way. Sometimes the logger is used to log a simple error message and in other - * situations sql and bindings are passed. - * The log method should be able to accept all kinds of parameters and data by using - * functions like func_num_args/func_get_args. - * - * @return void - */ - public function log(); -} - - -class RedBean_Logger_Default implements RedBean_Logger -{ - - /** - * Default logger method logging to STDOUT. - * This is the default/reference implementation of a logger. - * This method will write the message value to STDOUT (screen). - * - * @param $message (optional) - * - * @return void - */ - public function log() - { - if ( func_num_args() < 1 ) return; - - foreach ( func_get_args() as $argument ) { - if ( is_array( $argument ) ) { - echo print_r( $argument, TRUE ); - } else { - echo $argument; - } - - echo "
\n"; - } - } -} - - -interface RedBean_BeanHelper +namespace RedBeanPHP { + +use RedBeanPHP\ToolBox as ToolBox; +use RedBeanPHP\OODBBean as OODBBean; + +/** + * Bean Helper Interface + * + * @file RedBean/IBeanHelper.php + * @desc Interface for Bean Helper. + * @author Gabor de Mooij and the RedBeanPHP Community + * @license BSD/GPLv2 + * + * Interface for Bean Helper. + * A little bolt that glues the whole machinery together. + * + * copyright (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community + * This source file is subject to the BSD/GPLv2 License that is bundled + * with this source code in the file license.txt. + */ +interface BeanHelper { /** @@ -7832,7 +8351,7 @@ interface RedBean_BeanHelper * as such the bean is a proxy for OODB. This allows beans to implement * their magic getters and setters and return lists. * - * @return RedBean_Toolbox $toolbox toolbox + * @return ToolBox $toolbox toolbox */ public function getToolbox(); @@ -7850,59 +8369,152 @@ interface RedBean_BeanHelper * Given a certain bean this method will * return the corresponding model. * - * @param RedBean_OODBBean $bean + * @param OODBBean $bean * * @return string */ - public function getModelForBean( RedBean_OODBBean $bean ); + public function getModelForBean( OODBBean $bean ); +} } +namespace RedBeanPHP\BeanHelper { -class RedBean_BeanHelper_Facade implements RedBean_BeanHelper +use RedBeanPHP\BeanHelper as BeanHelper; +use RedBeanPHP\Facade as Facade; +use RedBeanPHP\OODBBean as OODBBean; +use RedBeanPHP\SimpleModelHelper as SimpleModelHelper; + +/** + * Bean Helper. + * The Bean helper helps beans to access access the toolbox and + * FUSE models. This Bean Helper makes use of the facade to obtain a + * reference to the toolbox. + * + * @file RedBean/BeanHelperFacade.php + * @desc Finds the toolbox for the bean. + * @author Gabor de Mooij and the RedBeanPHP Community + * @license BSD/GPLv2 + * + * (c) copyright G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community + * This source file is subject to the BSD/GPLv2 License that is bundled + * with this source code in the file license.txt. + */ +class SimpleFacadeBeanHelper implements BeanHelper { /** - * @see RedBean_BeanHelper::getToolbox + * Factory function to create instance of Simple Model, if any. + * + * @var closure + */ + private static $factory = null; + + /** + * @see BeanHelper::getToolbox */ public function getToolbox() { - return RedBean_Facade::$toolbox; + return Facade::getToolBox(); } /** - * @see RedBean_BeanHelper::getModelForBean + * @see BeanHelper::getModelForBean */ - public function getModelForBean( RedBean_OODBBean $bean ) + public function getModelForBean( OODBBean $bean ) { - $modelName = RedBean_ModelHelper::getModelName( $bean->getMeta( 'type' ), $bean ); + $model = $bean->getMeta( 'type' ); + $prefix = defined( 'REDBEAN_MODEL_PREFIX' ) ? REDBEAN_MODEL_PREFIX : '\\Model_'; - if ( !class_exists( $modelName ) ) { - return NULL; + if ( strpos( $model, '_' ) !== FALSE ) { + $modelParts = explode( '_', $model ); + $modelName = ''; + foreach( $modelParts as $part ) { + $modelName .= ucfirst( $part ); + } + $modelName = $prefix . $modelName; + + if ( !class_exists( $modelName ) ) { + //second try + $modelName = $prefix . ucfirst( $model ); + + if ( !class_exists( $modelName ) ) { + return NULL; + } + } + + } else { + + $modelName = $prefix . ucfirst( $model ); + if ( !class_exists( $modelName ) ) { + return NULL; + } } - - $obj = RedBean_ModelHelper::factory( $modelName ); + $obj = self::factory( $modelName ); $obj->loadBean( $bean ); return $obj; } /** - * @see RedBean_BeanHelper::getExtractedToolbox + * @see BeanHelper::getExtractedToolbox */ public function getExtractedToolbox() { - $toolbox = $this->getToolbox(); - - return array( $toolbox->getRedBean(), $toolbox->getDatabaseAdapter(), $toolbox->getWriter(), $toolbox ); + return Facade::getExtractedToolbox(); } + + /** + * Factory method using a customizable factory function to create + * the instance of the Simple Model. + * + * @param string $modelClassName name of the class + * + * @return SimpleModel + */ + public static function factory( $modelClassName ) + { + $factory = self::$factory; + return ( $factory ) ? $factory( $modelClassName ) : new $modelClassName(); + } + + /** + * Sets the factory function to create the model when using FUSE + * to connect a bean to a model. + * + * @param closure $factory + * + * @return void + */ + public static function setFactoryFunction( $factory ) + { + self::$factory = $factory; + } + } +} +namespace RedBeanPHP { -class RedBean_SimpleModel +use RedBeanPHP\OODBBean as OODBBean; + +/** + * SimpleModel + * Base Model For All RedBeanPHP Models using FUSE. + * + * @file RedBean/SimpleModel.php + * @desc Part of FUSE + * @author Gabor de Mooij and the RedBeanPHP Team + * @license BSD/GPLv2 + * + * copyright (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community + * This source file is subject to the BSD/GPLv2 License that is bundled + * with this source code in the file license.txt. + */ +class SimpleModel { /** - * @var RedBean_OODBBean + * @var OODBBean */ protected $bean; @@ -7910,11 +8522,11 @@ class RedBean_SimpleModel * Used by FUSE: the ModelHelper class to connect a bean to a model. * This method loads a bean in the model. * - * @param RedBean_OODBBean $bean bean + * @param OODBBean $bean bean * * @return void */ - public function loadBean( RedBean_OODBBean $bean ) + public function loadBean( OODBBean $bean ) { $this->bean = $bean; } @@ -7971,7 +8583,7 @@ class RedBean_SimpleModel * You can box your beans before passing them to functions or methods * with typed parameters. * - * @return RedBean_SimpleModel + * @return SimpleModel */ public function box() { @@ -7982,438 +8594,103 @@ class RedBean_SimpleModel * Unbox the bean from the model. * This method returns the bean inside the model. * - * @return RedBean_OODBBean + * @return OODBBean */ public function unbox() { return $this->bean; } } +} +namespace RedBeanPHP { -class RedBean_ModelHelper implements RedBean_Observer +use RedBeanPHP\Observer as Observer; +use RedBeanPHP\OODBBean as OODBBean; +use RedBeanPHP\Observable as Observable; + +/** + * RedBean Model Helper + * + * @file RedBean/ModelHelper.php + * @desc Connects beans to models, in essence + * @author Gabor de Mooij and the RedBeanPHP Community + * @license BSD/GPLv2 + * + * This is the core of so-called FUSE. + * + * copyright (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community + * This source file is subject to the BSD/GPLv2 License that is bundled + * with this source code in the file license.txt. + */ +class SimpleModelHelper implements Observer { /** - * @var RedBean_IModelFormatter - */ - private static $modelFormatter; - - /** - * @var RedBean_DependencyInjector - */ - private static $dependencyInjector; - - /** - * @see RedBean_Observer::onEvent + * @see Observer::onEvent */ public function onEvent( $eventName, $bean ) { $bean->$eventName(); } - /** - * Given a model ID (model identifier) this method returns the - * full model name. - * - * @param string $model - * @param RedBean_OODBBean $bean - * - * @return string - */ - public static function getModelName( $model, $bean = NULL ) - { - if ( self::$modelFormatter ) { - return self::$modelFormatter->formatModel( $model, $bean ); - } else { - $prefix = defined('REDBEAN_MODEL_PREFIX') ? REDBEAN_MODEL_PREFIX : 'Model_'; - - return $prefix . ucfirst( $model ); - } - } - - /** - * Sets the model formatter to be used to discover a model - * for Fuse. - * - * @param string $modelFormatter - * - * @return void - */ - public static function setModelFormatter( $modelFormatter ) - { - self::$modelFormatter = $modelFormatter; - } - - /** - * Obtains a new instance of $modelClassName, using a dependency injection - * container if possible. - * - * @param string $modelClassName name of the model - * - * @return object - */ - public static function factory( $modelClassName ) - { - if ( self::$dependencyInjector ) { - return self::$dependencyInjector->getInstance( $modelClassName ); - } - - return new $modelClassName(); - } - - /** - * Sets the dependency injector to be used. - * - * @param RedBean_DependencyInjector $di injector to be used - * - * @return void - */ - public static function setDependencyInjector( RedBean_DependencyInjector $di ) - { - self::$dependencyInjector = $di; - } - - /** - * Stops the dependency injector from resolving dependencies. Removes the - * reference to the dependency injector. - * - * @return void - */ - public static function clearDependencyInjector() - { - self::$dependencyInjector = NULL; - } - /** * Attaches the FUSE event listeners. Now the Model Helper will listen for * CRUD events. If a CRUD event occurs it will send a signal to the model * that belongs to the CRUD bean and this model will take over control from * there. * - * @param RedBean_Observable $observable + * @param Observable $observable * * @return void */ - public function attachEventListeners( RedBean_Observable $observable ) + public function attachEventListeners( Observable $observable ) { foreach ( array( 'update', 'open', 'delete', 'after_delete', 'after_update', 'dispense' ) as $e ) { $observable->addEventListener( $e, $this ); } } } +} +namespace RedBeanPHP { -class RedBean_SQLHelper +use RedBeanPHP\ToolBox as ToolBox; +use RedBeanPHP\AssociationManager as AssociationManager; +use RedBeanPHP\OODBBean as OODBBean; + +/** + * RedBean Tag Manager. + * The tag manager offers an easy way to quickly implement basic tagging + * functionality. + * + * @file RedBean/TagManager.php + * @desc RedBean Tag Manager + * @author Gabor de Mooij and the RedBeanPHP community + * @license BSD/GPLv2 + * + * Provides methods to tag beans and perform tag-based searches in the + * bean database. + * + * copyright (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community + * This source file is subject to the BSD/GPLv2 License that is bundled + * with this source code in the file license.txt. + */ +class TagManager { /** - * @var RedBean_Adapter - */ - protected $adapter; - - /** - * @var boolean - */ - protected $capture = FALSE; - - /** - * @var string - */ - protected $sql = ''; - - /** - * @var boolean - */ - protected static $flagUseCamelCase = TRUE; - - /** - * @var array - */ - protected $params = array(); - - /** - * Toggles support for camelCased statements. - * If set to TRUE this will turn camelCase into spaces. - * For instance leftJoin becomes - * 'left join'. - * - * @param boolean $yesNo TRUE to use camelcase mode - * - * @return void - */ - public static function useCamelCase( $yesNo ) - { - self::$flagUseCamelCase = (boolean) $yesNo; - } - - /** - * Constructor. - * Allows you to mix PHP and SQL as if they were one language. - * - * @param RedBean_Adapter_DBAdapter $adapter database adapter for querying - */ - public function __construct( RedBean_Adapter $adapter ) - { - $this->adapter = $adapter; - } - - /** - * Magic method to construct SQL query. - * Accepts any kind of message and turns it into an SQL statement and - * adds it to the query string. - * If camelcase is set to TRUE camelCase transitions will be turned into spaces. - * Underscores will be replaced with spaces as well. - * Arguments will be imploded using a comma as glue character and are also added - * to the query. - * - * If capture mode is on, this method returns a reference to itself allowing - * chaining. - * - * If capture mode if off, this method will immediately exceute the resulting - * SQL query and return a string result. - * - * @param string $funcName name of the next SQL statement/keyword - * @param array $args list of statements to be seperated by commas - * - * @return string|RedBean_SQLHelper - */ - public function __call( $funcName, $args = array() ) - { - if ( self::$flagUseCamelCase ) { - static $funcCache = array(); - - if ( !isset( $funcCache[$funcName] ) ) { - $funcCache[$funcName] = strtolower( preg_replace( '/(?<=[a-z])([A-Z])|([A-Z])(?=[a-z])/', '_$1$2', $funcName ) ); - } - - $funcName = $funcCache[$funcName]; - } - - $funcName = str_replace( '_', ' ', $funcName ); - - if ( $this->capture ) { - $this->sql .= ' ' . $funcName . ' ' . implode( ',', $args ); - - return $this; - } else { - return $this->adapter->getCell( 'SELECT ' . $funcName . '(' . implode( ',', $args ) . ')' ); - } - } - - /** - * Begins SQL query. - * Turns on capture mode. The helper will now postpone execution of the - * resulting SQL until the get() method has been invoked. - * - * @return RedBean_SQLHelper - */ - public function begin() - { - $this->capture = TRUE; - - return $this; - } - - /** - * Adds a value to the parameter list. - * This method adds a value to the list of parameters that will be bound - * to the SQL query. Chainable. - * - * @param mixed $param parameter to be added - * - * @return RedBean_SQLHelper - */ - public function put( $param ) - { - $this->params[] = $param; - - return $this; - } - - /** - * Executes query and returns the result. - * In capture mode this method will execute the query you have build using - * this helper and return the result. - * The parameter determines how to retrieve the results from the query. - * Possible options are: 'cell', 'row', 'col' or 'all'. - * Use cell to obtain a single cell, row for a row, col for a column and all for - * a multidimensional array. - * - * @param string $retrieval One of these 'cell', 'row', 'col' or 'all'. - * - * @return mixed $result - */ - public function get( $what = '' ) - { - $what = 'get' . ucfirst( $what ); - - $rs = $this->adapter->$what( $this->sql, $this->params ); - - $this->clear(); - - return $rs; - } - - /** - * Clears the parameter list as well as the SQL query string. - * - * @return RedBean_SQLHelper - */ - public function clear() - { - $this->sql = ''; - $this->params = array(); - $this->capture = FALSE; //turn off capture mode (issue #142) - - return $this; - } - - /** - * To explicitly add a piece of SQL. - * - * @param string $sql sql - * - * @return RedBean_SQLHelper - */ - public function addSQL( $sql ) - { - if ( $this->capture ) { - $this->sql .= ' ' . $sql . ' '; - } - - return $this; - } - - /** - * Returns query parts. - * This method returns the query parts in an array. - * This method returns an array with the following format: - * - * array( - * string $sqlStatementString, - * array $parameters - * ) - * - * @return array - */ - public function getQuery() - { - $list = array( $this->sql, $this->params ); - $this->clear(); - - return $list; - } - - /** - * Nests another query builder query in the current query. - * - * @param RedBean_SQLHelper - * - * @return RedBean_SQLHelper - */ - public function nest( RedBean_SQLHelper $sqlHelper ) - { - list( $sql, $params ) = $sqlHelper->getQuery(); - - $this->sql .= $sql; - - $this->params += $params; - - return $this; - } - - /** - * Writes a '(' to the sql query. - * - * @return RedBean_SQLHelper - */ - public function open() - { - if ( $this->capture ) { - $this->sql .= ' ( '; - } - - return $this; - } - - /** - * Writes a ')' to the sql query. - * - * @return RedBean_SQLHelper - */ - public function close() - { - if ( $this->capture ) { - $this->sql .= ' ) '; - } - - return $this; - } - - /** - * Generates question mark slots for an array of values. - * For each entry of the array this method generates a single - * question mark character slot. Finally the slots are glued - * separated by commas and returned as a single string. - * - * @param array $array Array with values to generate slots for - * - * @return string - */ - public function genSlots( $array ) - { - if ( is_array( $array ) && count( $array ) > 0 ) { - $filler = array_fill( 0, count( $array ), '?' ); - - return implode( ',', $filler ); - } else { - return ''; - } - } - - /** - * Returns a new SQL Helper with the same adapter as the current one. - * - * @return RedBean_SQLHelper - */ - public function getNew() - { - return new self( $this->adapter ); - } - - /** - * When cast to string, simply print the query and its bindings. - * - * @return string - */ - public function __toString() - { - list( $query, $params ) = $this->getQuery(); - - return print_r( array( - 'query' => $query, - 'params' => $params - ), true ); - } -} - - -class RedBean_TagManager -{ - - /** - * @var RedBean_Toolbox + * @var ToolBox */ protected $toolbox; /** - * @var RedBean_AssociationManager + * @var AssociationManager */ protected $associationManager; /** - * @var RedBean_OODBBean + * @var OODBBean */ protected $redbean; @@ -8442,9 +8719,9 @@ class RedBean_TagManager * The tag manager offers an easy way to quickly implement basic tagging * functionality. * - * @param RedBean_Toolbox $toolbox + * @param ToolBox $toolbox */ - public function __construct( RedBean_Toolbox $toolbox ) + public function __construct( ToolBox $toolbox ) { $this->toolbox = $toolbox; $this->redbean = $toolbox->getRedBean(); @@ -8458,7 +8735,7 @@ class RedBean_TagManager * * @param string $title title * - * @return RedBean_OODBBean + * @return OODBBean */ protected function findTagByTitle( $title ) { @@ -8485,7 +8762,7 @@ class RedBean_TagManager * Tag list can be either an array with tag names or a comma separated list * of tag names. * - * @param RedBean_OODBBean $bean bean to check for tags + * @param OODBBean $bean bean to check for tags * @param array|string $tags list of tags * @param boolean $all whether they must all match or just some * @@ -8512,7 +8789,7 @@ class RedBean_TagManager * Tag list can be either an array with tag names or a comma separated list * of tag names. * - * @param RedBean_OODBBean $bean tagged bean + * @param OODBBean $bean tagged bean * @param array|string $tagList list of tags (names) * * @return void @@ -8539,21 +8816,16 @@ class RedBean_TagManager * Tag list can be either an array with tag names or a comma separated list * of tag names. * - * @param RedBean_OODBBean $bean bean to be tagged + * @param OODBBean $bean bean to be tagged * @param array|string $tagList a list of tags * * @return array */ - public function tag( RedBean_OODBBean $bean, $tagList = NULL ) + public function tag( OODBBean $bean, $tagList = NULL ) { if ( is_null( $tagList ) ) { - $tags = array(); - $keys = $this->associationManager->related( $bean, 'tag' ); - - if ( $keys ) { - $tags = $this->redbean->batch( 'tag', $keys ); - } - + + $tags = $bean->sharedTag; $foundTags = array(); foreach ( $tags as $tag ) { @@ -8578,12 +8850,12 @@ class RedBean_TagManager * Tag list can be either an array with tag names or a comma separated list * of tag names. * - * @param RedBean_OODBBean $bean bean to add tags to + * @param OODBBean $bean bean to add tags to * @param array|string $tagList list of tags to add to bean * * @return void */ - public function addTags( RedBean_OODBBean $bean, $tagList ) + public function addTags( OODBBean $bean, $tagList ) { $tags = $this->extractTagsIfNeeded( $tagList ); @@ -8606,79 +8878,77 @@ class RedBean_TagManager /** * Returns all beans that have been tagged with one or more * of the specified tags. - * + * * Tag list can be either an array with tag names or a comma separated list * of tag names. * * @param string $beanType type of bean you are looking for * @param array|string $tagList list of tags to match + * @param string $sql additional SQL (use only for pagination) + * @param array $bindings bindings * * @return array */ - public function tagged( $beanType, $tagList ) + public function tagged( $beanType, $tagList, $sql = '', $bindings = array() ) { $tags = $this->extractTagsIfNeeded( $tagList ); + $records = $this->toolbox->getWriter()->queryTagged( $beanType, $tags, FALSE, $sql, $bindings ); - $collection = array(); - - $tags = $this->redbean->find( 'tag', array( 'title' => $tags ) ); - - if ( is_array( $tags ) && count( $tags ) > 0 ) { - $collectionKeys = $this->associationManager->related( $tags, $beanType ); - - if ( $collectionKeys ) { - $collection = $this->redbean->batch( $beanType, $collectionKeys ); - } - } - - return $collection; + return $this->redbean->convertToBeans( $beanType, $records ); } /** * Returns all beans that have been tagged with ALL of the tags given. - * + * * Tag list can be either an array with tag names or a comma separated list * of tag names. - * + * * @param string $beanType type of bean you are looking for * @param array|string $tagList list of tags to match * * @return array */ - public function taggedAll( $beanType, $tagList ) + public function taggedAll( $beanType, $tagList, $sql = '', $bindings = array() ) { $tags = $this->extractTagsIfNeeded( $tagList ); + $records = $this->toolbox->getWriter()->queryTagged( $beanType, $tags, TRUE, $sql, $bindings ); - $beans = array(); - foreach ( $tags as $tag ) { - $beans = $this->tagged( $beanType, $tag ); - - if ( isset( $oldBeans ) ) { - $beans = array_intersect_assoc( $beans, $oldBeans ); - } - - $oldBeans = $beans; - } - - return $beans; + return $this->redbean->convertToBeans( $beanType, $records ); } } +} +namespace RedBeanPHP { -class RedBean_LabelMaker +use RedBeanPHP\ToolBox as ToolBox; +use RedBeanPHP\OODBBean as OODBBean; + +/** + * Label Maker + * + * @file RedBean/LabelMaker.php + * @desc Makes so-called label beans + * @author Gabor de Mooij and the RedBeanPHP Community + * @license BSD/GPLv2 + * + * copyright (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community + * This source file is subject to the BSD/GPLv2 License that is bundled + * with this source code in the file license.txt. + */ +class LabelMaker { /** - * @var RedBean_Toolbox + * @var ToolBox */ protected $toolbox; /** * Constructor. * - * @param RedBean_ToolBox $toolbox + * @param ToolBox $toolbox */ - public function __construct( RedBean_ToolBox $toolbox ) + public function __construct( ToolBox $toolbox ) { $this->toolbox = $toolbox; } @@ -8734,7 +9004,7 @@ class RedBean_LabelMaker * * @param string $enum ENUM specification for label * - * @return array|RedBean_OODBBean + * @return array|OODBBean */ public function enum( $enum ) { @@ -8766,298 +9036,129 @@ class RedBean_LabelMaker return $newEnumItem; } } - - -class RedBean_Finder -{ - - /** - * @var RedBean_ToolBox - */ - protected $toolbox; - - /** - * @var RedBean_OODB - */ - protected $redbean; - - /** - * Constructor. - * The Finder requires a toolbox. - * - * @param RedBean_ToolBox $toolbox - */ - public function __construct( RedBean_ToolBox $toolbox ) - { - $this->toolbox = $toolbox; - $this->redbean = $toolbox->getRedBean(); - } - - /** - * Finds a bean using a type and a where clause (SQL). - * As with most Query tools in RedBean you can provide values to - * be inserted in the SQL statement by populating the value - * array parameter; you can either use the question mark notation - * or the slot-notation (:keyname). - * - * @param string $type type the type of bean you are looking for - * @param string $sql sql SQL query to find the desired bean, starting right after WHERE clause - * @param array $bindings values array of values to be bound to parameters in query - * - * @return array - * - * @throws RedBean_Exception_Security - */ - public function find( $type, $sql = NULL, $bindings = array() ) - { - if ( $sql instanceof RedBean_SQLHelper ) { - list( $sql, $bindings ) = $sql->getQuery(); - } - - if ( !is_array( $bindings ) ) { - throw new RedBean_Exception_Security( - 'Expected array, ' . gettype( $bindings ) . ' given.' - ); - } - - return $this->redbean->find( $type, array(), $sql, $bindings ); - } - - /** - * @see RedBean_Finder::find - * The variation also exports the beans (i.e. it returns arrays). - * - * @param string $type type the type of bean you are looking for - * @param string $sql sql SQL query to find the desired bean, starting right after WHERE clause - * @param array $bindings values array of values to be bound to parameters in query - * - * @return array - */ - public function findAndExport( $type, $sql = NULL, $bindings = array() ) - { - $arr = array(); - foreach ( $this->find( $type, $sql, $bindings ) as $key => $item ) { - $arr[$key] = $item->export(); - } - - return $arr; - } - - /** - * @see RedBean_Finder::find - * This variation returns the first bean only. - * - * @param string $type type the type of bean you are looking for - * @param string $sql sql SQL query to find the desired bean, starting right after WHERE clause - * @param array $bindings values array of values to be bound to parameters in query - * - * @return RedBean_OODBBean - */ - public function findOne( $type, $sql = NULL, $bindings = array() ) - { - $items = $this->find( $type, $sql, $bindings ); - - if ( empty($items) ) { - return NULL; - } - - return reset( $items ); - } - - /** - * @see RedBean_Finder::find - * This variation returns the last bean only. - * - * @param string $type the type of bean you are looking for - * @param string $sql SQL query to find the desired bean, starting right after WHERE clause - * @param array $bindings values array of values to be bound to parameters in query - * - * @return RedBean_OODBBean - */ - public function findLast( $type, $sql = NULL, $bindings = array() ) - { - $items = $this->find( $type, $sql, $bindings ); - - if ( empty($items) ) { - return NULL; - } - - return end( $items ); - } - - /** - * @see RedBean_Finder::find - * Convience method. Tries to find beans of a certain type, - * if no beans are found, it dispenses a bean of that type. - * - * @param string $type the type of bean you are looking for - * @param string $sql SQL query to find the desired bean, starting right after WHERE clause - * @param array $bindings values array of values to be bound to parameters in query - * - * @return array - */ - public function findOrDispense( $type, $sql = NULL, $bindings = array() ) - { - $foundBeans = $this->find( $type, $sql, $bindings ); - - if ( empty( $foundBeans ) ) { - return array( $this->redbean->dispense( $type ) ); - } else { - return $foundBeans; - } - } - - /** - * Returns the bean identified by the RESTful path. - * For instance: - * - * $user - * /site/1/page/3 - * - * returns page with ID 3 in ownPage of site 1 in ownSite of - * $user bean. - * - * Works with shared lists as well: - * - * $user - * /site/1/page/3/shared-ad/4 - * - * Note that this method will open all intermediate beans so you can - * attach access control rules to each bean in the path. - * - * @param RedBean_OODBBean $bean - * @param array $steps (an array representation of a REST path) - * - * @return RedBean_OODBBean - * - * @throws RedBean_Exception_Security - */ - public function findByPath( $bean, $steps ) - { - $numberOfSteps = count( $steps ); - - if ( !$numberOfSteps ) return $bean; - - if ( $numberOfSteps % 2 ) { - throw new RedBean_Exception_Security( 'Invalid path: needs 1 more element.' ); - } - - for ( $i = 0; $i < $numberOfSteps; $i += 2 ) { - $steps[$i] = trim( $steps[$i] ); - - if ( $steps[$i] === '' ) { - throw new RedBean_Exception_Security( 'Cannot access list.' ); - } - - if ( strpos( $steps[$i], 'shared-' ) === FALSE ) { - $listName = 'own' . ucfirst( $steps[$i] ); - $listType = $this->toolbox->getWriter()->esc( $steps[$i] ); - } else { - $listName = 'shared' . ucfirst( substr( $steps[$i], 7 ) ); - $listType = $this->toolbox->getWriter()->esc( substr( $steps[$i], 7 ) ); - } - - $list = $bean->withCondition( " {$listType}.id = ? ", array( $steps[$i + 1] ) )->$listName; - - if ( !isset( $list[$steps[$i + 1]] ) ) { - throw new RedBean_Exception_Security( 'Cannot access bean.' ); - } - - $bean = $list[$steps[$i + 1]]; - } - - return $bean; - } } +namespace RedBeanPHP { -class RedBean_Facade +use RedBeanPHP\ToolBox as ToolBox; +use RedBeanPHP\OODB as OODB; +use RedBeanPHP\QueryWriter as QueryWriter; +use RedBeanPHP\Adapter\DBAdapter as DBAdapter; +use RedBeanPHP\AssociationManager as AssociationManager; +use RedBeanPHP\TagManager as TagManager; +use RedBeanPHP\DuplicationManager as DuplicationManager; +use RedBeanPHP\LabelMaker as LabelMaker; +use RedBeanPHP\Finder as Finder; +use RedBeanPHP\RedException\SQL as SQL; +use RedBeanPHP\RedException\Security as Security; +use RedBeanPHP\Logger as Logger; +use RedBeanPHP\Logger\RDefault as RDefault; +use RedBeanPHP\Logger\RDefault\Debug as Debug; +use RedBeanPHP\OODBBean as OODBBean; +use RedBeanPHP\SimpleModel as SimpleModel; +use RedBeanPHP\SimpleModelHelper as SimpleModelHelper; +use RedBeanPHP\Adapter as Adapter; +use RedBeanPHP\QueryWriter\AQueryWriter as AQueryWriter; +use RedBeanPHP\RedException as RedException; +use RedBeanPHP\BeanHelper\SimpleFacadeBeanHelper as SimpleFacadeBeanHelper; +use RedBeanPHP\Driver\RPDO as RPDO; + +/** + * RedBean Facade + * + * Version Information + * RedBean Version @version 4.1 + * + * @file RedBean/Facade.php + * @desc Convenience class for RedBeanPHP. + * @author Gabor de Mooij and the RedBeanPHP Community + * @license BSD/GPLv2 + * + * This class hides the object landscape of + * RedBeanPHP behind a single letter class providing + * almost all functionality with simple static calls. + * + * copyright (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community + * This source file is subject to the BSD/GPLv2 License that is bundled + * with this source code in the file license.txt. + */ +class Facade { /** * RedBeanPHP version constant. */ - const C_REDBEANPHP_VERSION = '3.5'; - - /** - * @var boolean - */ - private static $strictType = TRUE; + const C_REDBEANPHP_VERSION = '4.1'; /** * @var array */ - public static $toolboxes = array(); + private static $toolboxes = array(); /** - * @var RedBean_ToolBox + * @var ToolBox */ - public static $toolbox; + private static $toolbox; /** - * @var RedBean_OODB + * @var OODB */ - public static $redbean; + private static $redbean; /** - * @var RedBean_QueryWriter + * @var QueryWriter */ - public static $writer; + private static $writer; /** - * @var RedBean_Adapter_DBAdapter + * @var DBAdapter */ - public static $adapter; + private static $adapter; /** - * @var RedBean_AssociationManager + * @var AssociationManager */ - public static $associationManager; + private static $associationManager; /** - * @var RedBean_AssociationManager_ExtAssociationManager + * @var TagManager */ - public static $extAssocManager; + private static $tagManager; /** - * @var RedBean_TagManager + * @var DuplicationManager */ - public static $tagManager; + private static $duplicationManager; /** - * @var RedBean_DuplicationManager + * @var LabelMaker */ - public static $duplicationManager; + private static $labelMaker; /** - * @var RedBean_LabelMaker + * @var Finder */ - public static $labelMaker; - - /** - * @var RedBean_Finder - */ - public static $finder; + private static $finder; /** * @var string */ - public static $currentDB = ''; - - /** - * @var RedBean_SQLHelper - */ - public static $f; + private static $currentDB = ''; /** * @var array */ - public static $plugins = array(); + private static $plugins = array(); + + /** + * @var string + */ + private static $exportCaseStyle = 'default'; /** * Internal Query function, executes the desired query. Used by * all facade query functions. This keeps things DRY. * - * @throws RedBean_Exception_SQL + * @throws SQL * * @param string $method desired query method (i.e. 'cell', 'col', 'exec' etc..) * @param string $sql the sql you want to execute @@ -9069,12 +9170,12 @@ class RedBean_Facade { if ( !self::$redbean->isFrozen() ) { try { - $rs = self::$adapter->$method( $sql, $bindings ); - } catch ( RedBean_Exception_SQL $exception ) { + $rs = Facade::$adapter->$method( $sql, $bindings ); + } catch ( SQL $exception ) { if ( self::$writer->sqlStateIn( $exception->getSQLState(), array( - RedBean_QueryWriter::C_SQLSTATE_NO_SUCH_COLUMN, - RedBean_QueryWriter::C_SQLSTATE_NO_SUCH_TABLE ) + QueryWriter::C_SQLSTATE_NO_SUCH_COLUMN, + QueryWriter::C_SQLSTATE_NO_SUCH_TABLE ) ) ) { return ( $method === 'getCell' ) ? NULL : array(); @@ -9085,7 +9186,7 @@ class RedBean_Facade return $rs; } else { - return self::$adapter->$method( $sql, $bindings ); + return Facade::$adapter->$method( $sql, $bindings ); } } @@ -9103,63 +9204,21 @@ class RedBean_Facade } /** - * Turns an array (post/request array) into a collection of beans. - * Handy for turning forms into bean structures that can be stored with a - * single call. + * Tests the connection. + * Returns TRUE if connection has been established and + * FALSE otherwise. * - * Typical usage: - * - * $struct = R::graph($_POST); - * R::store($struct); - * - * Example of a valid array: - * - * $form = array( - * 'type' => 'order', - * 'ownProduct' => array( - * array('id' => 171, 'type' => 'product'), - * ), - * 'ownCustomer' => array( - * array('type' => 'customer', 'name' => 'Bill') - * ), - * 'sharedCoupon' => array( - * array('type' => 'coupon', 'name' => '123'), - * array('type' => 'coupon', 'id' => 3) - * ) - * ); - * - * Each entry in the array will become a property of the bean. - * The array needs to have a type-field indicating the type of bean it is - * going to be. The array can have nested arrays. A nested array has to be - * named conform the bean-relation conventions, i.e. ownPage/sharedPage - * each entry in the nested array represents another bean. - * - * @param array $array array to be turned into a bean collection - * @param boolean $filterEmpty whether you want to exclude empty beans - * - * @return array - * - * @throws RedBean_Exception_Security + * @return boolean */ - public static function graph( $array, $filterEmpty = FALSE ) + public static function testConnection() { - $c = new RedBean_Plugin_Cooker; - $c->setToolbox( self::$toolbox ); - return $c->graph( $array, $filterEmpty); - } + if ( !isset( self::$adapter ) ) return FALSE; - /** - * Logs queries beginning with CREATE or ALTER to file (TimeLine). - * Attaches a listener to the adapter to monitor for schema altering queries. - * - * @param string $filename destination file - * - * @return void - */ - public static function log($filename) - { - $tl = new RedBean_Plugin_TimeLine($filename); - self::$adapter->addEventListener('sql_exec', $tl); + $database = self::$adapter->getDatabase(); + try { + @$database->connect(); + } catch ( \Exception $e ) {} + return $database->isConnected(); } /** @@ -9172,7 +9231,7 @@ class RedBean_Facade * @param string $password Password for database * @param boolean $frozen TRUE if you want to setup in frozen mode * - * @return RedBean_ToolBox + * @return ToolBox */ public static function setup( $dsn = NULL, $username = NULL, $password = NULL, $frozen = FALSE ) { @@ -9186,10 +9245,23 @@ class RedBean_Facade return self::$toolbox; } + /** + * Toggles Narrow Field Mode. + * See documentation in QueryWriter. + * + * @param boolean $mode TRUE = Narrow Field Mode + * + * @return void + */ + public static function setNarrowFieldMode( $mode ) + { + AQueryWriter::setNarrowFieldMode( $mode ); + } + /** * Starts a transaction within a closure (or other valid callback). - * If an Exception is thrown inside, the operation is automatically rolled back. - * If no Exception happens, it commits automatically. + * If an\Exception is thrown inside, the operation is automatically rolled back. + * If no\Exception happens, it commits automatically. * It also supports (simulated) nested transactions (that is useful when * you have many methods that needs transactions but are unaware of * each other). @@ -9212,7 +9284,7 @@ class RedBean_Facade * * @param callable $callback Closure (or other callable) with the transaction logic * - * @throws RedBean_Exception_Security + * @throws Security * * @return mixed * @@ -9220,7 +9292,7 @@ class RedBean_Facade public static function transaction( $callback ) { if ( !is_callable( $callback ) ) { - throw new RedBean_Exception_Security( 'R::transaction needs a valid callback.' ); + throw new RedException( 'R::transaction needs a valid callback.' ); } static $depth = 0; @@ -9235,7 +9307,7 @@ class RedBean_Facade if ( $depth == 0 ) { self::commit(); } - } catch ( Exception $exception ) { + } catch (\Exception $exception ) { $depth--; if ( $depth == 0 ) { self::rollback(); @@ -9265,13 +9337,35 @@ class RedBean_Facade * * @return void */ - public static function addDatabase( $key, $dsn, $user = NULL, $pass = NULL, $frozen = FALSE, $autoSetEncoding = TRUE ) + public static function addDatabase( $key, $dsn, $user = NULL, $pass = NULL, $frozen = FALSE ) { if ( isset( self::$toolboxes[$key] ) ) { - throw new RedBean_Exception_Security( 'A database has already be specified for this key.' ); + throw new RedException( 'A database has already be specified for this key.' ); } - - self::$toolboxes[$key] = RedBean_Setup::kickstart( $dsn, $user, $pass, $frozen, $autoSetEncoding ); + + if ( is_object($dsn) ) { + $db = new RPDO( $dsn ); + $dbType = $db->getDatabaseType(); + } else { + $db = new RPDO( $dsn, $user, $pass, TRUE ); + $dbType = substr( $dsn, 0, strpos( $dsn, ':' ) ); + } + + $adapter = new DBAdapter( $db ); + + $writers = array('pgsql' => 'PostgreSQL', + 'sqlite' => 'SQLiteT', + 'cubrid' => 'CUBRID', + 'mysql' => 'MySQL'); + + $wkey = trim( strtolower( $dbType ) ); + if ( !isset( $writers[$wkey] ) ) trigger_error( 'Unsupported DSN: '.$wkey ); + $writerClass = '\\RedBeanPHP\\QueryWriter\\'.$writers[$wkey]; + $writer = new $writerClass( $adapter ); + $redbean = new OODB( $writer ); + + $redbean->freeze( ( $frozen === TRUE ) ); + self::$toolboxes[$key] = new ToolBox( $redbean, $adapter, $writer ); } /** @@ -9306,23 +9400,31 @@ class RedBean_Facade * be printed to the screen or logged by provided logger. * If no database connection has been configured using R::setup() or * R::selectDatabase() this method will throw an exception. + * Returns the attached logger instance. * - * @param boolean $tf - * @param RedBean_Logger $logger + * @param boolean $tf + * @param integer $mode (0 = to STDOUT, 1 = to ARRAY) * - * @throws RedBean_Exception_Security + * @throws Security + * + * @return Logger\RDefault */ - public static function debug( $tf = TRUE, $logger = NULL ) + public static function debug( $tf = TRUE, $mode = 0 ) { - if ( !$logger ) { - $logger = new RedBean_Logger_Default; + if ($mode > 1) { + $mode -= 2; + $logger = new Debug; + } else { + $logger = new RDefault; } if ( !isset( self::$adapter ) ) { - throw new RedBean_Exception_Security( 'Use R::setup() first.' ); + throw new RedException( 'Use R::setup() first.' ); } - + $logger->setMode($mode); self::$adapter->getDatabase()->setDebugMode( $tf, $logger ); + + return $logger; } /** @@ -9343,7 +9445,7 @@ class RedBean_Facade /** * Stores a bean in the database. This method takes a - * RedBean_OODBBean Bean Object $bean and stores it + * OODBBean Bean Object $bean and stores it * in the database. If the database schema is not compatible * with this bean and RedBean runs in fluid mode the schema * will be altered to store the bean correctly. @@ -9355,11 +9457,11 @@ class RedBean_Facade * The return value is an integer if possible. If it is not possible to * represent the value as an integer a string will be returned. * - * @param RedBean_OODBBean|RedBean_SimpleModel $bean bean to store + * @param OODBBean|SimpleModel $bean bean to store * * @return integer|string * - * @throws RedBean_Exception_Security + * @throws Security */ public static function store( $bean ) { @@ -9393,7 +9495,7 @@ class RedBean_Facade * @param string|array $types * @param mixed $id * - * @return RedBean_OODBBean + * @return OODBBean */ public static function loadMulti( $types, $id ) { @@ -9414,13 +9516,13 @@ class RedBean_Facade /** * Loads a bean from the object database. - * It searches for a RedBean_OODBBean Bean Object in the + * It searches for a OODBBean Bean Object in the * database. It does not matter how this bean has been stored. * RedBean uses the primary key ID $id and the string $type * to find the bean. The $type specifies what kind of bean you * are looking for; this is the same type as used with the * dispense() function. If RedBean finds the bean it will return - * the RedBean_OODB Bean object; if it cannot find the bean + * the OODB Bean object; if it cannot find the bean * RedBean will return a new bean of type $type and with * primary key ID 0. In the latter case it acts basically the * same as dispense(). @@ -9432,9 +9534,9 @@ class RedBean_Facade * @param string $type type of bean you want to load * @param integer $id ID of the bean you want to load * - * @throws RedBean_Exception_SQL + * @throws SQL * - * @return RedBean_OODBBean + * @return OODBBean */ public static function load( $type, $id ) { @@ -9443,14 +9545,14 @@ class RedBean_Facade /** * Removes a bean from the database. - * This function will remove the specified RedBean_OODBBean + * This function will remove the specified OODBBean * Bean Object from the database. * - * @param RedBean_OODBBean|RedBean_SimpleModel $bean bean you want to remove from database + * @param OODBBean|SimpleModel $bean bean you want to remove from database * * @return void * - * @throws RedBean_Exception_Security + * @throws Security */ public static function trash( $bean ) { @@ -9461,20 +9563,36 @@ class RedBean_Facade * Dispenses a new RedBean OODB Bean for use with * the rest of the methods. * - * @param string $type type - * @param integer $number number of beans to dispense + * @param string|array $typeOrBeanArray type or bean array to import + * @param integer $number number of beans to dispense + * @param boolean $alwaysReturnArray if TRUE always returns the result as an array * - * @return array|RedBean_OODBBean + * @return array|OODBBean * - * @throws RedBean_Exception_Security + * @throws Security */ - public static function dispense( $type, $num = 1 ) + public static function dispense( $typeOrBeanArray, $num = 1, $alwaysReturnArray = FALSE ) { - if ( !preg_match( '/^[a-z0-9]+$/', $type ) && self::$strictType ) { - throw new RedBean_Exception_Security( 'Invalid type: ' . $type ); + if ( is_array($typeOrBeanArray) ) { + if ( !isset( $typeOrBeanArray['_type'] ) ) throw new RedException('Missing _type field.'); + $import = $typeOrBeanArray; + $type = $import['_type']; + unset( $import['_type'] ); + } else { + $type = $typeOrBeanArray; } - return self::$redbean->dispense( $type, $num ); + if ( !preg_match( '/^[a-z0-9]+$/', $type ) ) { + throw new RedException( 'Invalid type: ' . $type ); + } + + $beanOrBeans = self::$redbean->dispense( $type, $num, $alwaysReturnArray ); + + if ( isset( $import ) ) { + $beanOrBeans->import( $import ); + } + + return $beanOrBeans; } /** @@ -9496,11 +9614,12 @@ class RedBean_Facade * This returns an array with a book bean and then another array * containing 100 page beans. * - * @param string $order a description of the desired dispense order using the syntax above + * @param string $order a description of the desired dispense order using the syntax above + * @param boolean $onlyArrays return only arrays even if amount < 2 * * @return array */ - public static function dispenseAll( $order ) + public static function dispenseAll( $order, $onlyArrays = FALSE ) { $list = array(); @@ -9513,24 +9632,12 @@ class RedBean_Facade $amount = 1; } - $list[] = self::dispense( $type, $amount ); + $list[] = self::dispense( $type, $amount, $onlyArrays ); } return $list; } - /** - * Toggles strict bean type names. - * If set to TRUE (default) this will forbid the use of underscores and - * uppercase characters in bean type strings (R::dispense). - * - * @param boolean - */ - public static function setStrictTyping( $trueFalse ) - { - self::$strictType = (bool) $trueFalse; - } - /** * Convience method. Tries to find beans of a certain type, * if no beans are found, it dispenses a bean of that type. @@ -9546,175 +9653,6 @@ class RedBean_Facade return self::$finder->findOrDispense( $type, $sql, $bindings ); } - /** - * Associates two Beans. This method will associate two beans with eachother. - * You can then get one of the beans by using the related() function and - * providing the other bean. You can also provide a base bean in the extra - * parameter. This base bean allows you to add extra information to the association - * record. Note that this is for advanced use only and the information will not - * be added to one of the beans, just to the association record. - * It's also possible to provide an array or JSON string as base bean. If you - * pass a scalar this function will interpret the base bean as having one - * property called 'extra' with the value of the scalar. - * - * @todo extract from facade - * - * @param RedBean_OODBBean $bean1 bean that will be part of the association - * @param RedBean_OODBBean $bean2 bean that will be part of the association - * @param mixed $extra bean, scalar, array or JSON providing extra data. - * - * @return mixed - */ - public static function associate( $beans1, $beans2, $extra = NULL ) - { - if ( !$extra ) { - return self::$associationManager->associate( $beans1, $beans2 ); - } else { - return self::$extAssocManager->extAssociateSimple( $beans1, $beans2, $extra ); - } - } - - /** - * Breaks the association between two beans. - * This functions breaks the association between a pair of beans. After - * calling this functions the beans will no longer be associated with - * eachother. Calling related() with either one of the beans will no longer - * return the other bean. - * - * @param RedBean_OODBBean $bean1 bean - * @param RedBean_OODBBean $bean2 bean - * - * @return mixed - */ - public static function unassociate( $beans1, $beans2, $fast = FALSE ) - { - self::$associationManager->unassociate( $beans1, $beans2, $fast ); - } - - /** - * Returns all the beans associated with $bean. - * This method will return an array containing all the beans that have - * been associated once with the associate() function and are still - * associated with the bean specified. The type parameter indicates the - * type of beans you are looking for. You can also pass some extra SQL and - * values for that SQL to filter your results after fetching the - * related beans. - * - * Don't try to make use of subqueries, a subquery using IN() seems to - * be slower than two queries! - * - * Since 3.2, you can now also pass an array of beans instead just one - * bean as the first parameter. - * - * @param RedBean_OODBBean|array $bean the bean you have, the reference bean - * @param string $type the type of beans you want to search for - * @param string $sql SQL snippet for extra filtering - * @param array $bindings values to be inserted in SQL slots - * - * @return array - */ - public static function related( $bean, $type, $sql = '', $bindings = array() ) - { - return self::$associationManager->relatedSimple( $bean, $type, $sql, $bindings ); - } - - /** - * Counts the number of related beans in an N-M relation. - * Counts the number of beans of type $type that are related to $bean, - * using optional filtering SQL $sql with $bindings. This count will - * only search for N-M associated beans (works like countShared). - * The $bean->countShared() method is the preferred way to obtain this - * number. - * - * @warning not a preferred method, use $bean->countShared if possible. - * - * @param RedBean_OODBBean $bean the bean you have, the reference bean - * @param string $type the type of bean you want to count - * @param string $sql SQL snippet for extra filtering - * @param array $bindings values to be inserted in SQL slots - * - * @return integer - */ - public static function relatedCount( $bean, $type, $sql = NULL, $bindings = array() ) - { - return self::$associationManager->relatedCount( $bean, $type, $sql, $bindings ); - } - - /** - * Returns only a single associated bean. - * This works just like R::related but returns a single bean. Which bean will be - * returned depends on the SQL snippet provided. - * For more details refer to R::related. - * - * @warning not a preferred method, use $bean->shared if possible. - * - * @param RedBean_OODBBean $bean the bean you have, the reference bean - * @param string $type type of bean you are searching for - * @param string $sql SQL for extra filtering - * @param array $bindings values to be inserted in SQL slots - * - * @return RedBean_OODBBean - */ - public static function relatedOne( RedBean_OODBBean $bean, $type, $sql = NULL, $bindings = array() ) - { - return self::$associationManager->relatedOne( $bean, $type, $sql, $bindings ); - } - - /** - * Returns only the last associated bean. - * This works just like R::related but returns a single bean, the last one. - * If the query result contains multiple beans, the last bean from this result set will be returned. - * For more details refer to R::related. - * - * @warning not a preferred method, use $bean->shared if possible. - * - * @param RedBean_OODBBean $bean bean provided - * @param string $type type of bean you are searching for - * @param string $sql SQL for extra filtering - * @param array $bindings values to be inserted in SQL slots - * - * @return RedBean_OODBBean - */ - public static function relatedLast( RedBean_OODBBean $bean, $type, $sql = NULL, $bindings = array() ) - { - return self::$associationManager->relatedLast( $bean, $type, $sql, $bindings ); - } - - /** - * Checks whether a pair of beans is related N-M. This function does not - * check whether the beans are related in N:1 way. - * The name may be bit confusing because two beans can be related in - * various ways. This method only checks for many-to-many relations, for other - * relations please use $bean->ownX where X is the type of the bean you are - * looking for. - * - * @param RedBean_OODBBean $bean1 first bean - * @param RedBean_OODBBean $bean2 second bean - * - * @return boolean - */ - public static function areRelated( RedBean_OODBBean $bean1, RedBean_OODBBean $bean2 ) - { - return self::$associationManager->areRelated( $bean1, $bean2 ); - } - - /** - * Clears all associated beans. - * Breaks all many-to-many associations of a bean and a specified type. - * Only breaks N-M relations. - * - * @warning not a preferred method, use $bean->shared = array() if possible. - * - * @param RedBean_OODBBean $bean bean you wish to clear many-to-many relations for - * @param string $type type of bean you wish to break associations with - * - * @return void - */ - public static function clearRelations( RedBean_OODBBean $bean, $type ) - { - self::$associationManager->clearRelations( $bean, $type ); - } - /** * Finds a bean using a type and a where clause (SQL). * As with most Query tools in RedBean you can provide values to @@ -9734,7 +9672,7 @@ class RedBean_Facade } /** - * @see RedBean_Facade::find + * @see Facade::find * The findAll() method differs from the find() method in that it does * not assume a WHERE-clause, so this is valid: * @@ -9754,7 +9692,7 @@ class RedBean_Facade } /** - * @see RedBean_Facade::find + * @see Facade::find * The variation also exports the beans (i.e. it returns arrays). * * @param string $type type the type of bean you are looking for @@ -9769,14 +9707,14 @@ class RedBean_Facade } /** - * @see RedBean_Facade::find + * @see Facade::find * This variation returns the first bean only. * * @param string $type type the type of bean you are looking for * @param string $sql sql SQL query to find the desired bean, starting right after WHERE clause * @param array $bindings values array of values to be bound to parameters in query * - * @return RedBean_OODBBean + * @return OODBBean */ public static function findOne( $type, $sql = NULL, $bindings = array() ) { @@ -9784,14 +9722,14 @@ class RedBean_Facade } /** - * @see RedBean_Facade::find + * @see Facade::find * This variation returns the last bean only. * * @param string $type type the type of bean you are looking for * @param string $sql sql SQL query to find the desired bean, starting right after WHERE clause * @param array $bindings values array of values to be bound to parameters in query * - * @return RedBean_OODBBean + * @return OODBBean */ public static function findLast( $type, $sql = NULL, $bindings = array() ) { @@ -9818,7 +9756,7 @@ class RedBean_Facade } /** - * @see RedBean_Facade::batch + * @see Facade::batch * * Alias for batch(). Batch method is older but since we added so-called *All * methods like storeAll, trashAll, dispenseAll and findAll it seemed logical to @@ -9954,19 +9892,53 @@ class RedBean_Facade * Note: * This function does a reflectional database query so it may be slow. * - * @param RedBean_OODBBean $bean bean to be copied - * @param array $trail for internal usage, pass array() - * @param boolean $pid for internal usage + * @deprecated + * This function is deprecated in favour of R::duplicate(). + * This function has a confusing method signature, the R::duplicate() function + * only accepts two arguments: bean and filters. + * + * @param OODBBean $bean bean to be copied + * @param array $trail for internal usage, pass array() + * @param boolean $pid for internal usage + * @param array $white white list filter with bean types to duplicate * * @return array */ public static function dup( $bean, $trail = array(), $pid = FALSE, $filters = array() ) { self::$duplicationManager->setFilters( $filters ); - return self::$duplicationManager->dup( $bean, $trail, $pid ); } + /** + * Makes a deep copy of a bean. This method makes a deep copy + * of the bean.The copy will have the following: + * + * - All beans in own-lists will be duplicated as well + * - All references to shared beans will be copied but not the shared beans themselves + * - All references to parent objects (_id fields) will be copied but not the parents themselves + * + * In most cases this is the desired scenario for copying beans. + * This function uses a trail-array to prevent infinite recursion, if a recursive bean is found + * (i.e. one that already has been processed) the ID of the bean will be returned. + * This should not happen though. + * + * Note: + * This function does a reflectional database query so it may be slow. + * + * Note: + * This is a simplified version of the deprecated R::dup() function. + * + * @param OODBBean $bean bean to be copied + * @param array $white white list filter with bean types to duplicate + * + * @return array + */ + public static function duplicate( $bean, $filters = array() ) + { + return self::dup( $bean, array(), FALSE, $filters ); + } + /** * Exports a collection of beans. Handy for XML/JSON exports with a * Javascript framework like Dojo or ExtJS. @@ -9975,30 +9947,39 @@ class RedBean_Facade * - all own bean lists (recursively) * - all shared beans (not THEIR own lists) * - * @param array|RedBean_OODBBean $beans beans to be exported - * @param boolean $parents whether you want parent beans to be exported - * @param array $filters whitelist of types + * @param array|OODBBean $beans beans to be exported + * @param boolean $parents whether you want parent beans to be exported + * @param array $filters whitelist of types * * @return array */ - public static function exportAll( $beans, $parents = FALSE, $filters = array() ) + public static function exportAll( $beans, $parents = FALSE, $filters = array()) { - return self::$duplicationManager->exportAll( $beans, $parents, $filters ); + return self::$duplicationManager->exportAll( $beans, $parents, $filters, self::$exportCaseStyle ); } /** - * @deprecated - * Given two beans and a property this method will - * swap the values of the property in the beans. + * Selects case style for export. + * This will determine the case style for the keys of exported beans (see exportAll). + * The following options are accepted: * - * @param array $beans beans to swap property values of - * @param string $property property whose value you want to swap + * 'default' RedBeanPHP by default enforces Snake Case (i.e. book_id is_valid ) + * 'camel' Camel Case (i.e. bookId isValid ) + * 'dolphin' Dolphin Case (i.e. bookID isValid ) Like CamelCase but ID is written all uppercase + * + * @warning RedBeanPHP transforms camelCase to snake_case using a slightly different + * algorithm, it also converts isACL to is_acl (not is_a_c_l) and bookID to book_id. + * Due to information loss this cannot be corrected. However if you might try + * DolphinCase for IDs it takes into account the exception concerning IDs. + * + * @param string $caseStyle case style identifier * * @return void */ - public static function swap( $beans, $property ) + public static function useExportCase( $caseStyle = 'default' ) { - self::$associationManager->swap( $beans, $property ); + if ( !in_array( $caseStyle, array( 'default', 'camel', 'dolphin' ) ) ) throw new RedException( 'Invalid case selected.' ); + self::$exportCaseStyle = $caseStyle; } /** @@ -10028,7 +10009,7 @@ class RedBean_Facade * method will return TRUE if one of the tags matches, FALSE if none * match. * - * @param RedBean_OODBBean $bean bean to check for tags + * @param OODBBean $bean bean to check for tags * @param array $tags list of tags * @param boolean $all whether they must all match or just some * @@ -10044,7 +10025,7 @@ class RedBean_Facade * Removes all specified tags from the bean. The tags specified in * the second parameter will no longer be associated with the bean. * - * @param RedBean_OODBBean $bean tagged bean + * @param OODBBean $bean tagged bean * @param array $tagList list of tags (names) * * @return void @@ -10063,12 +10044,12 @@ class RedBean_Facade * be associated with the bean. * You may also pass an array instead of a string. * - * @param RedBean_OODBBean $bean bean + * @param OODBBean $bean bean * @param mixed $tagList tags * * @return string */ - public static function tag( RedBean_OODBBean $bean, $tagList = NULL ) + public static function tag( OODBBean $bean, $tagList = NULL ) { return self::$tagManager->tag( $bean, $tagList ); } @@ -10080,12 +10061,12 @@ class RedBean_Facade * be associated with the bean. * You may also pass an array instead of a string. * - * @param RedBean_OODBBean $bean bean + * @param OODBBean $bean bean * @param array $tagList list of tags to add to bean * * @return void */ - public static function addTags( RedBean_OODBBean $bean, $tagList ) + public static function addTags( OODBBean $bean, $tagList ) { self::$tagManager->addTags( $bean, $tagList ); } @@ -10096,12 +10077,14 @@ class RedBean_Facade * * @param string $beanType type of bean you are looking for * @param array $tagList list of tags to match + * @param string $sql additional SQL + * @param array $bindings bindings * * @return array */ - public static function tagged( $beanType, $tagList ) + public static function tagged( $beanType, $tagList, $sql = '', $bindings = array() ) { - return self::$tagManager->tagged( $beanType, $tagList ); + return self::$tagManager->tagged( $beanType, $tagList, $sql, $bindings ); } /** @@ -10110,12 +10093,14 @@ class RedBean_Facade * * @param string $beanType type of bean you are looking for * @param array $tagList list of tags to match + * @param string $sql additional SQL + * @param array $bindings bindings * * @return array */ - public static function taggedAll( $beanType, $tagList ) + public static function taggedAll( $beanType, $tagList, $sql = '', $bindings = array() ) { - return self::$tagManager->taggedAll( $beanType, $tagList ); + return self::$tagManager->taggedAll( $beanType, $tagList, $sql, $bindings ); } /** @@ -10127,7 +10112,7 @@ class RedBean_Facade */ public static function wipe( $beanType ) { - return self::$redbean->wipe( $beanType ); + return Facade::$redbean->wipe( $beanType ); } /** @@ -10141,11 +10126,11 @@ class RedBean_Facade * * @return integer * - * @throws RedBean_Exception_SQL + * @throws SQL */ public static function count( $type, $addSQL = '', $bindings = array() ) { - return self::$redbean->count( $type, $addSQL, $bindings ); + return Facade::$redbean->count( $type, $addSQL, $bindings ); } /** @@ -10153,11 +10138,11 @@ class RedBean_Facade * Adapter and you want it on-the-fly? Use this method to hot-swap your facade with a new * toolbox. * - * @param RedBean_ToolBox $tb toolbox + * @param ToolBox $tb toolbox * - * @return RedBean_ToolBox + * @return ToolBox */ - public static function configureFacadeWithToolbox( RedBean_ToolBox $tb ) + public static function configureFacadeWithToolbox( ToolBox $tb ) { $oldTools = self::$toolbox; @@ -10166,24 +10151,22 @@ class RedBean_Facade self::$writer = self::$toolbox->getWriter(); self::$adapter = self::$toolbox->getDatabaseAdapter(); self::$redbean = self::$toolbox->getRedBean(); - self::$finder = new RedBean_Finder( self::$toolbox ); + self::$finder = new Finder( self::$toolbox ); - self::$associationManager = new RedBean_AssociationManager( self::$toolbox ); + self::$associationManager = new AssociationManager( self::$toolbox ); self::$redbean->setAssociationManager( self::$associationManager ); - self::$labelMaker = new RedBean_LabelMaker( self::$toolbox ); - self::$extAssocManager = new RedBean_AssociationManager_ExtAssociationManager( self::$toolbox ); + self::$labelMaker = new LabelMaker( self::$toolbox ); - $helper = new RedBean_ModelHelper(); + $helper = new SimpleModelHelper(); $helper->attachEventListeners( self::$redbean ); - self::$associationManager->addEventListener( 'delete', $helper ); + self::$redbean->setBeanHelper( new SimpleFacadeBeanHelper ); - self::$duplicationManager = new RedBean_DuplicationManager( self::$toolbox ); - self::$tagManager = new RedBean_TagManager( self::$toolbox ); - self::$f = new RedBean_SQLHelper( self::$adapter ); + self::$duplicationManager = new DuplicationManager( self::$toolbox ); + self::$tagManager = new TagManager( self::$toolbox ); return $oldTools; } @@ -10257,7 +10240,7 @@ class RedBean_Facade */ public static function genSlots( $array ) { - return self::$f->genSlots( $array ); + return ( count( $array ) ) ? implode( ',', array_fill( 0, count( $array ), '?' ) ) : ''; } /** @@ -10276,39 +10259,6 @@ class RedBean_Facade } } - /** - * Sets a list of dependencies. - * A dependency list contains an entry for each dependent bean. - * A dependent bean will be removed if the relation with one of the - * dependencies gets broken. - * - * Example: - * - * array( - * 'page' => array('book', 'magazine') - * ) - * - * A page will be removed if: - * - * unset($book->ownPage[$pageID]); - * - * or: - * - * unset($magazine->ownPage[$pageID]); - * - * but not if: - * - * unset($paper->ownPage[$pageID]); - * - * @param array $dep list of dependencies - * - * @return void - */ - public static function dependencies( $dep ) - { - self::$redbean->setDepList( $dep ); - } - /** * Short hand function to store a set of beans at once, IDs will be * returned as an array. For information please consult the R::store() @@ -10405,7 +10355,7 @@ class RedBean_Facade * * @param string $enum either type or type-value * - * @return array|RedBean_OODBBean + * @return array|OODBBean */ public static function enum( $enum ) { @@ -10476,11 +10426,11 @@ class RedBean_Facade * Optional accessor for neat code. * Sets the database adapter you want to use. * - * @param RedBean_Adapter $adapter + * @param Adapter $adapter * * @return void */ - public static function setDatabaseAdapter( RedBean_Adapter $adapter ) + public static function setDatabaseAdapter( Adapter $adapter ) { self::$adapter = $adapter; } @@ -10489,11 +10439,11 @@ class RedBean_Facade * Optional accessor for neat code. * Sets the database adapter you want to use. * - * @param RedBean_QueryWriter $writer + * @param QueryWriter $writer * * @return void */ - public static function setWriter( RedBean_QueryWriter $writer ) + public static function setWriter( QueryWriter $writer ) { self::$writer = $writer; } @@ -10502,9 +10452,9 @@ class RedBean_Facade * Optional accessor for neat code. * Sets the database adapter you want to use. * - * @param RedBean_OODB $redbean + * @param OODB $redbean */ - public static function setRedBean( RedBean_OODB $redbean ) + public static function setRedBean( OODB $redbean ) { self::$redbean = $redbean; } @@ -10513,18 +10463,28 @@ class RedBean_Facade * Optional accessor for neat code. * Sets the database adapter you want to use. * - * @return RedBean_Adapter_DBAdapter + * @return DBAdapter */ public static function getDatabaseAdapter() { return self::$adapter; } + /** + * Returns the current duplication manager instance. + * + * @return DuplicationManager + */ + public static function getDuplicationManager() + { + return self::$duplicationManager; + } + /** * Optional accessor for neat code. * Sets the database adapter you want to use. * - * @return RedBean_QueryWriter + * @return QueryWriter */ public static function getWriter() { @@ -10535,7 +10495,7 @@ class RedBean_Facade * Optional accessor for neat code. * Sets the database adapter you want to use. * - * @return RedBean_OODB + * @return OODB */ public static function getRedBean() { @@ -10545,10 +10505,10 @@ class RedBean_Facade /** * Returns the toolbox currently used by the facade. * To set the toolbox use R::setup() or R::configureFacadeWithToolbox(). - * To create a toolbox use RedBean_Setup::kickstart(). Or create a manual - * toolbox using the RedBean_Toolbox class. + * To create a toolbox use Setup::kickstart(). Or create a manual + * toolbox using the ToolBox class. * - * @return RedBean_ToolBox + * @return ToolBox */ public static function getToolBox() { @@ -10556,69 +10516,32 @@ class RedBean_Facade } /** - * Preloads certain properties for beans. - * Understands aliases. + * Mostly for internal use, but might be handy + * for some users. + * This returns all the components of the currently + * selected toolbox. * - * Usage: + * Returns the components in the following order: * - * R::preload($books, 'author'); - * - * - preloads all the authors of all books, - * saves you a query per for-each iteration - * - * R::preload($books, array('coauthor'=>'author')); - * - * - same but with alias - * - * R::preload($texts,'page,page.book,page.book.author'); - * - * - preloads all pages for the texts, the books and the authors - * - * R::preload($texts,'page,*.book,*.author'); - * - * - same as above bit with short syntax (* means prefix with previous types) - * - * R::preload($p,'book,*.author,&.shelf'); - * - * - if author and shelf are on the same level use & instead of *. - * - * The other way around is possible as well, to load child beans in own-lists or - * shared-lists use: - * - * R::preload($books,'ownPage|page,sharedGenre|genre'); - * - * @param array $beans beans beans to use as a reference for preloading - * @param array|string $types types to load, either string or array + * 0 - OODB instance (getRedBean()) + * 1 - Database Adapter + * 2 - Query Writer + * 3 - Toolbox itself * * @return array */ - public static function preload( $beans, $types, $closure = NULL ) + public static function getExtractedToolbox() { - return self::$redbean->preload( $beans, $types, $closure ); + return array( + self::$redbean, + self::$adapter, + self::$writer, + self::$toolbox + ); } /** - * Alias for preload. - * Preloads certain properties for beans. - * Understands aliases. - * - * @see RedBean_Facade::preload - * - * Usage: R::preload($books, array('coauthor'=>'author')); - * - * @param array $beans beans beans to use as a reference for preloading - * @param array|string $types types to load, either string or array - * @param closure $closure function to call - * - * @return array - */ - public static function each( $beans, $types, $closure = NULL ) - { - return self::preload( $beans, $types, $closure ); - } - - /** - * Facade method for RedBean_QueryWriter_AQueryWriter::renameAssociation() + * Facade method for AQueryWriter::renameAssociation() * * @param string|array $from * @param string $to @@ -10627,7 +10550,7 @@ class RedBean_Facade */ public static function renameAssociation( $from, $to = NULL ) { - RedBean_QueryWriter_AQueryWriter::renameAssociation( $from, $to ); + AQueryWriter::renameAssociation( $from, $to ); } /** @@ -10650,6 +10573,64 @@ class RedBean_Facade return $list; } + /** + * Simple but effective debug function. + * Given a one or more beans this method will + * return an array containing first part of the string + * representation of each item in the array. + * + * @param OODBBean|array $data either a bean or an array of beans + * + * @return array + * + */ + public static function dump( $data ) + { + $array = array(); + + if ( $data instanceof OODBBean ) { + $str = strval( $data ); + if (strlen($str) > 35) { + $beanStr = substr( $str, 0, 35 ).'... '; + } else { + $beanStr = $str; + } + return $beanStr; + } + + if ( is_array( $data ) ) { + foreach( $data as $key => $item ) { + $array[$key] = self::dump( $item ); + } + } + + return $array; + } + + /** + * Binds an SQL function to a column. + * This method can be used to setup a decode/encode scheme or + * perform UUID insertion. This method is especially useful for handling + * MySQL spatial columns, because they need to be processed first using + * the asText/GeomFromText functions. + * + * Example: + * + * R::bindFunc( 'read', 'location.point', 'asText' ); + * R::bindFunc( 'write', 'location.point', 'GeomFromText' ); + * + * Passing NULL as the function will reset (clear) the function + * for this column/mode. + * + * @param string $mode (read or write) + * @param string $field + * @param string $function + * + */ + public static function bindFunc( $mode, $field, $function ) { + self::$redbean->bindFunc( $mode, $field, $function ); + } + /** * Dynamically extends the facade with a plugin. * Using this method you can register your plugin with the facade and then @@ -10670,7 +10651,7 @@ class RedBean_Facade public static function ext( $pluginName, $callable ) { if ( !ctype_alnum( $pluginName ) ) { - throw new RedBean_Exception( 'Plugin name may only contain alphanumeric characters.' ); + throw new RedException( 'Plugin name may only contain alphanumeric characters.' ); } self::$plugins[$pluginName] = $callable; } @@ -10687,1524 +10668,52 @@ class RedBean_Facade public static function __callStatic( $pluginName, $params ) { if ( !ctype_alnum( $pluginName) ) { - throw new RedBean_Exception( 'Plugin name may only contain alphanumeric characters.' ); + throw new RedException( 'Plugin name may only contain alphanumeric characters.' ); } if ( !isset( self::$plugins[$pluginName] ) ) { - throw new RedBean_Exception( 'Plugin \''.$pluginName.'\' does not exist, add this plugin using: R::ext(\''.$pluginName.'\')' ); + throw new RedException( 'Plugin \''.$pluginName.'\' does not exist, add this plugin using: R::ext(\''.$pluginName.'\')' ); } return call_user_func_array( self::$plugins[$pluginName], $params ); } } -//Compatibility with PHP 5.2 and earlier -if ( !function_exists( 'lcfirst' ) ) { - function lcfirst( $str ) { return (string) ( strtolower( substr( $str, 0, 1 ) ) . substr( $str, 1 ) ); } } - -interface RedBean_Plugin -{ -} - -; - - -class RedBean_Plugin_BeanCan implements RedBean_Plugin -{ - /** - * List of JSON RPC2 error code definitions. - */ - const C_JSONRPC2_PARSE_ERROR = -32700; - const C_JSONRPC2_INVALID_REQUEST = -32600; - const C_JSONRPC2_METHOD_NOT_FOUND = -32601; - const C_JSONRPC2_INVALID_PARAMETERS = -32602; - const C_JSONRPC2_INTERNAL_ERROR = -32603; - const C_JSONRPC2_SPECIFIED_ERROR = -32099; - - /** - * @var RedBean_ModelHelper - */ - private $modelHelper; - - /** - * @var array - */ - private $whitelist; - - /** - * Constructor. - */ - public function __construct() - { - $this->modelHelper = new RedBean_ModelHelper; - } - - /** - * Writes a response object for the client (JSON encoded). Internal method. - * - * @param mixed $result result - * @param integer $id request ID - * @param integer $errorCode error code from server - * @param string $errorMessage error message from server - * - * @return string $json - */ - private function resp( $result = NULL, $id = NULL, $errorCode = '-32603', $errorMessage = 'Internal Error' ) - { - $response = array( 'jsonrpc' => '2.0' ); - - if ( !is_null( $id ) ) $response['id'] = $id; - - if ( $result ) { - $response['result'] = $result; - } else { - $response['error'] = array( - 'code' => $errorCode, - 'message' => $errorMessage - ); - } - - return json_encode( $response ); - } - - /** - * Handles a JSON RPC 2 request to store a bean. - * - * @param string $id request ID, identification for request - * @param string $beanType type of the bean you want to store - * @param array $data data array - * - * @return string - */ - private function store( $id, $beanType, $data ) - { - if ( !isset( $data[0] ) ) { - return $this->resp( NULL, $id, self::C_JSONRPC2_INVALID_PARAMETERS, 'First param needs to be Bean Object' ); - } - - $data = $data[0]; - - if ( !isset( $data['id'] ) ) { - $bean = RedBean_Facade::dispense( $beanType ); - } else { - $bean = RedBean_Facade::load( $beanType, $data['id'] ); - } - - $bean->import( $data ); - - $rid = RedBean_Facade::store( $bean ); - - return $this->resp( $rid, $id ); - } - - /** - * Handles a JSON RPC 2 request to load a bean. - * - * @param string $id request ID, identification for request - * @param string $beanType type of the bean you want to store - * @param array $data data array containing the ID of the bean to load - * - * @return string - */ - private function load( $id, $beanType, $data ) - { - if ( !isset( $data[0] ) ) { - return $this->resp( NULL, $id, self::C_JSONRPC2_INVALID_PARAMETERS, 'First param needs to be Bean ID' ); - } - - $bean = RedBean_Facade::load( $beanType, $data[0] ); - - return $this->resp( $bean->export(), $id ); - } - - /** - * Handles a JSON RPC 2 request to trash a bean. - * - * @param string $id request ID, identification for request - * @param string $beanType type of the bean you want to delete - * @param array $data data array - * - * @return string - */ - private function trash( $id, $beanType, $data ) - { - if ( !isset( $data[0] ) ) { - return $this->resp( NULL, $id, self::C_JSONRPC2_INVALID_PARAMETERS, 'First param needs to be Bean ID' ); - } - - $bean = RedBean_Facade::load( $beanType, $data[0] ); - - RedBean_Facade::trash( $bean ); - - return $this->resp( 'OK', $id ); - } - - /** - * Handles a JSON RPC 2 request to export a bean. - * - * @param string $id request ID, identification for request - * @param string $beanType type of the bean you want to export - * @param array $data data array - * - * @return string - */ - private function export( $id, $beanType, $data ) - { - if ( !isset( $data[0] ) ) { - return $this->resp( NULL, $id, self::C_JSONRPC2_INVALID_PARAMETERS, 'First param needs to be Bean ID' ); - } - - $bean = RedBean_Facade::load( $beanType, $data[0] ); - - $array = RedBean_Facade::exportAll( array( $bean ), TRUE ); - - return $this->resp( $array, $id ); - } - - /** - * Handles a JSON RPC 2 request to perform a custom operation on a bean. - * - * @param string $id request ID, identification for request - * @param string $beanType type of the bean you want to store - * @param string $action action you want to invoke on bean model - * @param array $data data array - * - * @return string - */ - private function custom( $id, $beanType, $action, $data ) - { - $modelName = $this->modelHelper->getModelName( $beanType ); - - if ( !class_exists( $modelName ) ) { - return $this->resp( NULL, $id, self::C_JSONRPC2_METHOD_NOT_FOUND, 'No such bean in the can!' ); - } - - $beanModel = new $modelName; - - if ( !method_exists( $beanModel, $action ) ) { - return $this->resp( NULL, $id, self::C_JSONRPC2_METHOD_NOT_FOUND, "Method not found in Bean: $beanType " ); - } - - return $this->resp( call_user_func_array( array( $beanModel, $action ), $data ), $id ); - } - - /** - * Extracts bean type, action identifier, - * data array and method name from json array. - * - * @param array $jsonArray JSON array containing the details - * - * @return array - */ - private function getDataFromJSON( $jsonArray ) - { - $beanType = NULL; - $action = NULL; - - if ( !isset( $jsonArray['params'] ) ) { - $data = array(); - } else { - $data = $jsonArray['params']; - } - - //Check method signature - $method = explode( ':', trim( $jsonArray['method'] ) ); - - if ( count( $method ) === 2 ) { - //Collect Bean and Action - $beanType = $method[0]; - $action = $method[1]; - } - - return array( $beanType, $action, $data, $method ); - } - - /** - * Dispatches the JSON RPC request to one of the private methods. - * - * @param string $id identification of request - * @param string $beanType type of the bean you wish to apply the action to - * @param string $action action to apply - * @param array $data data array containing parameters or details - * - * @return array - */ - private function dispatch( $id, $beanType, $action, $data ) - { - try { - switch ( $action ) { - case 'store': - return $this->store( $id, $beanType, $data ); - case 'load': - return $this->load( $id, $beanType, $data ); - case 'trash': - return $this->trash( $id, $beanType, $data ); - case 'export': - return $this->export( $id, $beanType, $data ); - default: - return $this->custom( $id, $beanType, $action, $data ); - } - } catch ( Exception $exception ) { - return $this->resp( NULL, $id, self::C_JSONRPC2_SPECIFIED_ERROR, $exception->getCode() . '-' . $exception->getMessage() ); - } - } - - /** - * Sets a whitelist with format: array('beantype'=>array('update','customMethod')) etc. - * or simply string 'all' (for backward compatibility). - * - * @param array|string $whitelist a white list of beans and methods that should be accessible through the BeanCan Server. - * - * @return RedBean_Plugin_BeanCan - */ - public function setWhitelist( $whitelist ) - { - $this->whitelist = $whitelist; - - return $this; - } - - /** - * Processes a JSON object request. - * Second parameter can be a white list with format: array('beantype'=>array('update','customMethod')) etc. - * or simply string 'all' (for backward compatibility). - * - * @param array $jsonObject JSON request object - * @param array|string $whitelist a white list of beans and methods that should be accessible through the BeanCan Server. - * - * @return mixed $result result - */ - public function handleJSONRequest( $jsonString ) - { - if ( !$jsonArray = json_decode( $jsonString, TRUE ) ) { //Decode JSON string - return $this->resp( NULL, NULL, self::C_JSONRPC2_PARSE_ERROR, 'Cannot Parse JSON' ); - } - - if ( !isset( $jsonArray['jsonrpc'] ) ) { - return $this->resp( NULL, NULL, self::C_JSONRPC2_INVALID_REQUEST, 'No RPC version' ); - } - - if ( ( $jsonArray['jsonrpc'] != '2.0' ) ) { - return $this->resp( NULL, NULL, self::C_JSONRPC2_INVALID_REQUEST, 'Incompatible RPC Version' ); - } - - if ( !isset( $jsonArray['id'] ) ) { //DO we have an ID to identify this request? - return $this->resp( NULL, NULL, self::C_JSONRPC2_INVALID_REQUEST, 'No ID' ); - } - - $id = $jsonArray['id']; //Fetch the request Identification String. - - if ( !isset( $jsonArray['method'] ) ) { //Do we have a method? - return $this->resp( NULL, $id, self::C_JSONRPC2_INVALID_REQUEST, 'No method' ); - } - - list( $beanType, $action, $data, $method ) = $this->getDataFromJSON( $jsonArray ); //Do we have params? - - if ( count( $method ) !== 2 ) { - return $this->resp( NULL, $id, self::C_JSONRPC2_INVALID_REQUEST, 'Invalid method signature. Use: BEAN:ACTION' ); - } - - if ( !( $this->whitelist === 'all' || ( isset( $this->whitelist[$beanType] ) && in_array( $action, $this->whitelist[$beanType] ) ) ) ) { - return $this->resp( NULL, $id, self::C_JSONRPC2_INVALID_REQUEST, 'This bean is not available. Set whitelist to "all" or add to whitelist.' ); - } - - if ( preg_match( '/\W/', $beanType ) ) { //May not contain anything other than ALPHA NUMERIC chars and _ - return $this->resp( NULL, $id, self::C_JSONRPC2_INVALID_REQUEST, 'Invalid Bean Type String' ); - } - - if ( preg_match( '/\W/', $action ) ) { - return $this->resp( NULL, $id, self::C_JSONRPC2_INVALID_REQUEST, 'Invalid Action String' ); - } - - return $this->dispatch( $id, $beanType, $action, $data ); - } - - /** - * Support for RESTFul GET-requests. - * Only supports very BASIC REST requests, for more functionality please use - * the JSON-RPC 2 interface. - * - * @param string $pathToResource RESTFul path to resource - * - * @return string $json a JSON encoded response ready for sending to client - */ - public function handleRESTGetRequest( $pathToResource ) - { - if ( !is_string( $pathToResource ) ) { - return $this->resp( NULL, 0, self::C_JSONRPC2_SPECIFIED_ERROR, 'IR' ); - } - - $resourceInfo = explode( '/', $pathToResource ); - - $type = $resourceInfo[0]; - - try { - if ( count( $resourceInfo ) < 2 ) { - return $this->resp( RedBean_Facade::findAndExport( $type ) ); - } else { - $id = (int) $resourceInfo[1]; - - return $this->resp( RedBean_Facade::load( $type, $id )->export(), $id ); - } - } catch ( Exception $exception ) { - return $this->resp( NULL, 0, self::C_JSONRPC2_SPECIFIED_ERROR ); - } - } -} - - -class RedBean_Plugin_BeanCanResty implements RedBean_Plugin -{ - /** - * HTTP Error codes used by Resty BeanCan Server. - */ - const C_HTTP_BAD_REQUEST = 400; - const C_HTTP_FORBIDDEN_REQUEST = 403; - const C_HTTP_NOT_FOUND = 404; - const C_HTTP_INTERNAL_SERVER_ERROR = 500; - - /** - * @var RedBean_OODB - */ - private $oodb; - - /** - * @var RedBean_ToolBox - */ - private $toolbox; - - /** - * @var array - */ - private $whitelist; - - /** - * @var array - */ - private $sqlSnippets = array(); - - /** - * @var string - */ - private $method; - - /** - * @var array - */ - private $payload = array(); - - /** - * @var string - */ - private $uri; - - /** - * Reference bean, the bean used to find other beans in a REST request. - * All beans should be reachable given this root bean. - * - * @var RedBean_OODBBean - */ - private $root; - - /** - * Name of the currently selected list. - * - * @var string - */ - private $list; - - /** - * @var RedBean_OODBBean - */ - private $bean; - - /** - * Name of the type of the currently selected list. - * - * @var string - */ - private $type; - - /** - * Type of the currently selected bean. - * - * @var string - */ - private $beanType; - - /** - * List of bindings for the SQL snippet. - * - * @var array - */ - private $sqlBindings; - - /** - * An SQL snippet to sort or modify the contents of a list. - * - * @var string - */ - private $sqlSnippet; - - /** - * Writes a response object for the client (JSON encoded). Internal method. - * Returns a pseudo HTTP/REST response. You can refine or alter this response - * before sending it to the client. - * - * @param mixed $result result - * @param integer $errorCode error code from server - * @param string $errorMessage error message from server - * - * @return array $response - */ - private function resp( $result = NULL, $errorCode = '500', $errorMessage = 'Internal Error' ) - { - $response = array( 'red-resty' => '1.0' ); - - if ( $result !== NULL ) { - $response['result'] = $result; - } else { - $response['error'] = array( 'code' => $errorCode, 'message' => $errorMessage ); - } - - return $response; - } - - /** - * Handles a REST GET request. - * Returns the selected bean using the basic export method of the bean. - * Returns an array formatted according to RedBeanPHP REST BeanCan - * formatting specifications. - * - * @return array - */ - private function get() - { - return $this->resp( $this->bean->export() ); - } - - /** - * Handles a REST PUT request. - * Updates the bean described in the payload array in the database. - * Returns an array formatted according to RedBeanPHP REST BeanCan - * formatting specifications. - * - * Format of the payload array: - * - * array( - * 'bean' => array( property => value pairs ) - * ) - * - * @return array - */ - private function put() - { - if ( !isset( $this->payload['bean'] ) ) { - return $this->resp( NULL, self::C_HTTP_BAD_REQUEST, 'Missing parameter \'bean\'.' ); - } - - if ( !is_array( $this->payload['bean'] ) ) { - return $this->resp( NULL, self::C_HTTP_BAD_REQUEST, 'Parameter \'bean\' must be object/array.' ); - } - - foreach ( $this->payload['bean'] as $key => $value ) { - if ( !is_string( $key ) || !is_string( $value ) ) { - return $this->resp( NULL, self::C_HTTP_BAD_REQUEST, 'Object "bean" invalid.' ); - } - } - - $this->bean->import( $this->payload['bean'] ); - - $this->oodb->store( $this->bean ); - - $this->bean = $this->oodb->load( $this->bean->getMeta( 'type' ), $this->bean->id ); - - return $this->resp( $this->bean->export() ); - } - - /** - * Handles a REST POST request. - * Stores the bean described in the payload array in the database. - * Returns an array formatted according to RedBeanPHP REST BeanCan - * formatting specifications. - * - * Format of the payload array: - * - * array( - * 'bean' => array( property => value pairs ) - * ) - * - * @return array - */ - private function post() - { - if ( !isset( $this->payload['bean'] ) ) { - return $this->resp( NULL, self::C_HTTP_BAD_REQUEST, 'Missing parameter \'bean\'.' ); - } - - if ( !is_array( $this->payload['bean'] ) ) { - return $this->resp( NULL, self::C_HTTP_BAD_REQUEST, 'Parameter \'bean\' must be object/array.' ); - } - - foreach ( $this->payload['bean'] as $key => $value ) { - if ( !is_string( $key ) || !is_string( $value ) ) { - return $this->resp( NULL, self::C_HTTP_BAD_REQUEST, 'Object \'bean\' invalid.' ); - } - } - - $newBean = $this->oodb->dispense( $this->type ); - $newBean->import( $this->payload['bean'] ); - - if ( strpos( $this->list, 'shared-' ) === FALSE ) { - $listName = 'own' . ucfirst( $this->list ); - } else { - $listName = 'shared' . ucfirst( substr( $this->list, 7 ) ); - } - - array_push( $this->bean->$listName, $newBean ); - - $this->oodb->store( $this->bean ); - - $newBean = $this->oodb->load( $newBean->getMeta( 'type' ), $newBean->id ); - - return $this->resp( $newBean->export() ); - } - - /** - * Opens a list and returns the contents of the list. - * By default a list is interpreted as the own-list of the current bean. - * If the list begins with the prefix 'shared-' the shared list of the - * bean will be opened instead. Internal method. - * - * @return array - */ - private function openList() - { - $listOfBeans = array(); - - $listName = ( strpos( $this->list, 'shared-' ) === 0 ) ? ( 'shared' . ucfirst( substr( $this->list, 7 ) ) ) : ( 'own' . ucfirst( $this->list ) ); - - if ( $this->sqlSnippet ) { - if ( preg_match( '/^(ORDER|GROUP|HAVING|LIMIT|OFFSET|TOP)\s+/i', ltrim( $this->sqlSnippet ) ) ) { - $beans = $this->bean->with( $this->sqlSnippet, $this->sqlBindings )->$listName; - } else { - $beans = $this->bean->withCondition( $this->sqlSnippet, $this->sqlBindings )->$listName; - } - } else { - $beans = $this->bean->$listName; - } - - foreach ( $beans as $listBean ) { - $listOfBeans[] = $listBean->export(); - } - - return $this->resp( $listOfBeans ); - } - - /** - * Handles a REST DELETE request. - * Deletes the selected bean. - * Returns an array formatted according to RedBeanPHP REST BeanCan - * formatting specifications. Internal method. - * - * @return array - */ - private function delete() - { - $this->oodb->trash( $this->bean ); - - return $this->resp( 'OK' ); - } - - /** - * Handles a custom request method. - * Passes the arguments specified in 'param' to the method - * specified as request method of the selected bean. - * Returns an array formatted according to RedBeanPHP REST BeanCan - * formatting specifications. Internal method. - * - * Payload array: - * - * array('param' => array( - * param1, param2 etc.. - * )) - * - * @return array - */ - private function custom() - { - if ( !isset( $this->payload['param'] ) ) { - $this->payload['param'] = array(); - } - - if ( !is_array( $this->payload['param'] ) ) { - return $this->resp( NULL, self::C_HTTP_BAD_REQUEST, 'Parameter \'param\' must be object/array.' ); - } - - $answer = call_user_func_array( array( $this->bean, $this->method ), $this->payload['param'] ); - - return $this->resp( $answer ); - } - - /** - * Extracts SQL snippet and SQL bindings from the SQL bundle. - * Selects the appropriate SQL snippet for the list to be opened. - * - * @return void - */ - private function extractSQLSnippetsForGETList() - { - $sqlBundleItem = ( isset( $this->sqlSnippets[$this->list] ) ) ? $this->sqlSnippets[$this->list] : array( NULL, array() ); - - if ( isset( $sqlBundleItem[0] ) ) { - $this->sqlSnippet = $sqlBundleItem[0]; - } - - if ( isset( $sqlBundleItem[1] ) ) { - $this->sqlBindings = $sqlBundleItem[1]; - } - } - - /** - * Dispatches the REST request to the appropriate method. - * Returns a response array. - * - * @return array - */ - private function dispatch() - { - if ( $this->method == 'GET' ) { - if ( $this->list === NULL ) { - return $this->get(); - } - - return $this->openList(); - } elseif ( $this->method == 'DELETE' ) { - return $this->delete(); - } elseif ( $this->method == 'POST' ) { - return $this->post(); - } elseif ( $this->method == 'PUT' ) { - return $this->put(); - } - - return $this->custom(); - } - - /** - * Determines whether the bean type and action appear on the whitelist. - * - * @return boolean - */ - private function isOnWhitelist() - { - return ( - $this->whitelist === 'all' - || ( - $this->list === null - && isset( $this->whitelist[$this->beanType] ) - && in_array( $this->method, $this->whitelist[$this->beanType] ) - || ( - $this->list !== null - && isset( $this->whitelist[$this->type] ) - && in_array( $this->method, $this->whitelist[$this->type] ) - ) - ) - ); - } - - /** - * Finds a bean by its URI. - * Returns the bean identified by the specified URI. - * - * For more details - * @see RedBean_Finder::findByPath - * - * @return void - */ - private function findBeanByURI() - { - $finder = new RedBean_Finder( $this->toolbox ); - - $this->bean = $finder->findByPath( $this->root, $this->uri ); - $this->beanType = $this->bean->getMeta( 'type' ); - } - - /** - * Extract list information. - * Returns FALSE if the list cannot be read due to incomplete specification, i.e. - * less than one entry in the URI array. - * - * @return boolean - */ - private function extractListInfo() - { - if ( $this->method == 'POST' ) { - if ( count( $this->uri ) < 1 ) return FALSE; - - $this->list = array_pop( $this->uri ); - $this->type = ( strpos( $this->list, 'shared-' ) === 0 ) ? substr( $this->list, 7 ) : $this->list; - } elseif ( $this->method === 'GET' && count( $this->uri ) > 1 ) { - $lastItemInURI = $this->uri[count( $this->uri ) - 1]; - - if ( $lastItemInURI === 'list' ) { - array_pop( $this->uri ); - - $this->list = array_pop( $this->uri ); - $this->type = ( strpos( $this->list, 'shared-' ) === 0 ) ? substr( $this->list, 7 ) : $this->list; - - $this->extractSQLSnippetsForGETList(); - } - } - - return TRUE; - } - - /** - * Checks whether the URI contains invalid characters. - * - * @return boolean - */ - private function isURIValid() - { - if ( preg_match( '|^[\w\-/]*$|', $this->uri ) ) { - return FALSE; - } - - return TRUE; - } - - /** - * Extracts the URI. - * - * @return void - */ - private function extractURI() - { - $this->uri = ( ( strlen( $this->uri ) ) ) ? explode( '/', ( $this->uri ) ) : array(); - } - - /** - * Handles the REST request and returns a response array. - * - * @return array - */ - private function handleRESTRequest() - { - try { - if ( $this->isURIValid() ) { - return $this->resp( NULL, self::C_HTTP_BAD_REQUEST, 'URI contains invalid characters.' ); - } - - if ( !is_array( $this->payload ) ) { - return $this->resp( NULL, self::C_HTTP_BAD_REQUEST, 'Payload needs to be array.' ); - } - - $this->extractURI(); - - if ( $this->extractListInfo() === FALSE ) { - return $this->resp( NULL, self::C_HTTP_BAD_REQUEST, 'Missing list.' ); - } - - if ( !is_null( $this->type ) && !preg_match( '|^[\w]+$|', $this->type ) ) { - return $this->resp( NULL, self::C_HTTP_BAD_REQUEST, 'Invalid list.' ); - } - - try { - $this->findBeanByURI(); - } catch ( Exception $e ) { - return $this->resp( NULL, self::C_HTTP_NOT_FOUND, $e->getMessage() ); - } - - if ( !$this->isOnWhitelist() ) { - return $this->resp( NULL, self::C_HTTP_FORBIDDEN_REQUEST, 'This bean is not available. Set whitelist to "all" or add to whitelist.' ); - } - - return $this->dispatch(); - } catch ( Exception $e ) { - return $this->resp( NULL, self::C_HTTP_INTERNAL_SERVER_ERROR, 'Exception: ' . $e->getCode() ); - } - } - - /** - * Clears internal state of the REST BeanCan. - * - * @return void - */ - private function clearState() - { - $this->list = NULL; - $this->bean = NULL; - $this->type = NULL; - $this->beanType = NULL; - $this->sqlBindings = array(); - $this->sqlSnippet = NULL; - } - - /** - * Constructor. - * Creates a new instance of the Resty BeanCan Server. - * If no toolbox is provided the Resty BeanCan Server object will - * try to obtain the toolbox currently used by the RedBeanPHP facade. - * If you use only the R-methods and not the advanced objects this should be fine. - * - * @param RedBean_ToolBox $toolbox (optional) - */ - public function __construct( $toolbox = NULL ) - { - if ( $toolbox instanceof RedBean_ToolBox ) { - $this->toolbox = $toolbox; - $this->oodb = $toolbox->getRedBean(); - } else { - $this->toolbox = RedBean_Facade::getToolBox(); - $this->oodb = RedBean_Facade::getRedBean(); - } - } - - /** - * The Resty BeanCan uses a white list to determine whether the current - * request is allowed. - * - * A whitelist has the following format: - * - * array( 'book' - * => array( 'POST', 'GET', 'publish'), - * 'page' - * => etc... - * - * this will allow the methods 'POST', 'GET' and 'publish' for beans of type 'book'. - * To allow all methods on all beans pass the string 'all'. - * - * @param array|string $whitelist a white list of beans and methods that should be accessible through the BeanCan Server. - * - * @return RedBean_Plugin_BeanCan - */ - public function setWhitelist( $whitelist ) - { - $this->whitelist = $whitelist; - - return $this; - } - - /** - * Handles a REST request. - * Returns a JSON response string. - * - * The first argument need to be the reference bean, or root bean (for instance 'user 1'). - * The second argument is a path to select a bean relative to the root. - * For instance to select the 3rd page of a book of a user: 'book/1/page/3'. - * The third argument need to specify the REST method (GET/POST/DELETE/PUT) or NON-REST method - * (sendMail) to invoke. Optional arguments include the payload ($_POST) and - * a list of SQL snippets (the SQL bundle). The SQL bundle contains additional SQL and bindings - * per type, if a list gets accessed the SQL with the type-key of the list will be used to filter - * or sort the results. - * - * Only method-bean combinations mentioned in the whitelist will be allowed. - * Also note that handleREST accepts ALL kinds of methods. You can pass proper HTTP methods - * or fabricated methods. The latter will just cause the methods to be invoked on the specified beans. - * - * @param RedBean_OODBBean $root root bean for REST action - * @param string $uri the URI of the RESTful operation - * @param string $method the method you want to apply - * @param array $payload payload (for POSTs) - * @param array $sqlSnippets a bundle of SQL snippets to use - * - * @return string - */ - public function handleREST( $root, $uri, $method, $payload = array(), $sqlSnippets = array() ) - { - $this->sqlSnippets = $sqlSnippets; - $this->method = $method; - $this->payload = $payload; - $this->uri = $uri; - $this->root = $root; - - $this->clearState(); - - $result = $this->handleRESTRequest(); - - return $result; - } -} - -class RedBean_Plugin_QueryLogger implements RedBean_Observer, RedBean_Plugin +namespace RedBeanPHP { + +use RedBeanPHP\ToolBox as ToolBox; +use RedBeanPHP\AssociationManager as AssociationManager; +use RedBeanPHP\OODB as OODB; +use RedBeanPHP\OODBBean as OODBBean; +use RedBeanPHP\QueryWriter\AQueryWriter as AQueryWriter; + +/** + * Duplication Manager + * + * @file RedBean/DuplicationManager.php + * @desc Creates deep copies of beans + * @author Gabor de Mooij and the RedBeanPHP Community + * @license BSD/GPLv2 + * + * copyright (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community + * This source file is subject to the BSD/GPLv2 License that is bundled + * with this source code in the file license.txt. + */ +class DuplicationManager { /** - * @var array - */ - protected $logs = array(); - - /** - * Singleton pattern - * Constructor - private - */ - private function __construct() { } - - /** - * Creates a new instance of the Query Logger and attaches - * this logger to the adapter. - * - * @static - * - * @param RedBean_Observable $adapter the adapter you want to attach to - * - * @return RedBean_Plugin_QueryLogger - */ - public static function getInstanceAndAttach( RedBean_Observable $adapter ) - { - $queryLog = new RedBean_Plugin_QueryLogger; - - $adapter->addEventListener( 'sql_exec', $queryLog ); - - return $queryLog; - } - - /** - * Implementation of the onEvent() method for Observer interface. - * If a query gets executed this method gets invoked because the - * adapter will send a signal to the attached logger. - * - * @param string $eventName ID of the event (name) - * @param RedBean_Adapter_DBAdapter $adapter adapter that sends the signal - * - * @return void - */ - public function onEvent( $eventName, $adapter ) - { - if ( $eventName == 'sql_exec' ) { - $this->logs[] = $adapter->getSQL(); - } - } - - /** - * Searches the logs for the given word and returns the entries found in - * the log container. - * - * @param string $word word to look for - * - * @return array - */ - public function grep( $word ) - { - $found = array(); - foreach ( $this->logs as $log ) { - if ( strpos( $log, $word ) !== FALSE ) { - $found[] = $log; - } - } - - return $found; - } - - /** - * Returns all the logs. - * - * @return array - */ - public function getLogs() - { - return $this->logs; - } - - /** - * Clears the logs. - * - * @return void - */ - public function clear() - { - $this->logs = array(); - } -} - - -class RedBean_Plugin_Cooker implements RedBean_Plugin -{ - /** - * @var boolean - */ - private static $loadBeans = FALSE; - - /** - * @var boolean - */ - private static $useNULLForEmptyString = FALSE; - - /** - * @var RedBean_Toolbox - */ - private $toolbox; - - /** - * @var RedBean_OODB - */ - private $redbean; - - /** - * If you enable bean loading graph will load beans if there is an ID in the array. - * This is very powerful but can also cause security issues if a user knows how to - * manipulate beans and there is no model based ID validation. - * - * @param boolean $yesNo - * - * @return void - */ - public static function enableBeanLoading( $yesNo ) - { - self::$loadBeans = ( $yesNo ); - } - - /** - * Static version of setUseNullFlag. - * - * @param boolean $yesNo - * - * @return void - */ - public static function setUseNullFlagSt( $yesNo ) - { - self::$useNULLForEmptyString = (boolean) $yesNo; - } - - /** - * Sets the toolbox to be used by graph() - * - * @param RedBean_Toolbox $toolbox toolbox - * - * @return void - */ - public function setToolbox( RedBean_Toolbox $toolbox ) - { - $this->toolbox = $toolbox; - $this->redbean = $this->toolbox->getRedbean(); - } - - /** - * Loads bean, recurses if one of the property appears to be a list. - * - * @param array $array data array to import as a bean - * @param boolean $filterEmpty if TRUE empty STRING values are converted to NULL (default FALSE) - * - * @return RedBean_OODBBean - * - * @throws RedBean_Exception_Security - */ - private function loadBean( &$array, $filterEmpty ) - { - $type = $array['type']; - - unset( $array['type'] ); - - if ( isset( $array['id'] ) ) { // Do we need to load the bean? - if ( self::$loadBeans ) { - $bean = $this->redbean->load( $type, (int) $array['id'] ); - } else { - throw new RedBean_Exception_Security( 'Attempt to load a bean in Cooker. Use enableBeanLoading to override but please read security notices first.' ); - } - } else { - $bean = $this->redbean->dispense( $type ); - } - - foreach ( $array as $property => $value ) { - if ( is_array( $value ) ) { - $bean->$property = $this->graph( $value, $filterEmpty ); - } else { - $bean->$property = ( $value == '' && self::$useNULLForEmptyString ) ? NULL : $value; - } - } - - return $bean; - } - - /** - * Loads a list. Recurses for every bean in the list. - * - * @param array $array data array to import as a list - * @param boolean $filterEmpty if TRUE empty lists will NOT be imported - * - * @return array - * - * @throws RedBean_Exception_Security - */ - private function loadList( &$array, $filterEmpty ) - { - $beans = array(); - foreach ( $array as $key => $value ) { - $listBean = $this->graph( $value, $filterEmpty ); - - if ( !( $listBean instanceof RedBean_OODBBean ) ) { - throw new RedBean_Exception_Security( 'Expected bean but got :' . gettype( $listBean ) ); - } - - if ( $listBean->isEmpty() ) { - if ( !$filterEmpty ) { - $beans[$key] = $listBean; - } - } else { - $beans[$key] = $listBean; - } - } - - return $beans; - } - - /** - * Turns an array (post/request array) into a collection of beans. - * Handy for turning forms into bean structures that can be stored with a - * single call. - * - * Typical usage: - * - * $struct = R::graph($_POST); - * R::store($struct); - * - * Example of a valid array: - * - * $form = array( - * 'type' => 'order', - * 'ownProduct' => array( - * array('id' => 171, 'type' => 'product'), - * ), - * 'ownCustomer' => array( - * array('type' => 'customer', 'name' => 'Bill') - * ), - * 'sharedCoupon' => array( - * array('type' => 'coupon', 'name' => '123'), - * array('type' => 'coupon', 'id' => 3) - * ) - * ); - * - * Each entry in the array will become a property of the bean. - * The array needs to have a type-field indicating the type of bean it is - * going to be. The array can have nested arrays. A nested array has to be - * named conform the bean-relation conventions, i.e. ownPage/sharedPage - * each entry in the nested array represents another bean. - * - * @param array $array array to be turned into a bean collection - * @param boolean $filterEmpty whether you want to exclude empty beans - * - * @return array - * - * @throws RedBean_Exception_Security - */ - public function graph( $array, $filterEmpty = FALSE ) - { - if ( is_array( $array ) && isset( $array['type'] ) ) { - return $this->loadBean( $array, $filterEmpty ); - } elseif ( is_array( $array ) ) { - return $this->loadList( $array, $filterEmpty ); - } else { - throw new RedBean_Exception_Security( 'Expected array but got :' . gettype( $array ) ); - } - } - - /** - * Toggles the use-NULL flag. - * - * @param boolean $yesNo - * - * @return void - */ - public function setUseNullFlag( $yesNo ) - { - self::$useNULLForEmptyString = (bool) $yesNo; - } -} - - -class RedBean_Plugin_Cache extends RedBean_OODB implements RedBean_Plugin -{ - /** - * @var array - */ - protected $cache = array(); - - /** - * @var integer - */ - protected $hits = 0; - - /** - * @var integer - */ - protected $misses = 0; - - /** - * Constructor. - * Cache decorates RedBeanPHP OODB class, so needs a writer. - * - * @param RedBean_QueryWriter $writer - */ - public function __construct( RedBean_QueryWriter $writer ) - { - parent::__construct( $writer ); - } - - /** - * Loads a bean by type and id. If the bean cannot be found an - * empty bean will be returned instead. This is a cached version - * of the loader, if the bean has been cached it will be served - * from cache, otherwise the bean will be retrieved from the database - * as usual an a new cache entry will be added.. - * - * @param string $type type of bean you are looking for - * @param integer $id identifier of the bean - * - * @return RedBean_OODBBean $bean the bean object found - */ - public function load( $type, $id ) - { - if ( isset( $this->cache[$type][$id] ) ) { - $this->hits++; - $bean = $this->cache[$type][$id]; - } else { - $this->misses++; - - $bean = parent::load( $type, $id ); - - if ( $bean->id ) { - if ( !isset( $this->cache[$type] ) ) { - $this->cache[$type] = array(); - } - - $this->cache[$type][$id] = $bean; - } - } - - return $bean; - } - - /** - * Stores a RedBean OODBBean and caches it. - * - * @param RedBean_OODBBean $bean the bean you want to store - * - * @return mixed - */ - public function store( $bean ) - { - $id = parent::store( $bean ); - $type = $bean->getMeta( 'type' ); - - if ( !isset( $this->cache[$type] ) ) { - $this->cache[$type] = array(); - } - - $this->cache[$type][$id] = $bean; - - return $id; - } - - /** - * Trashes a RedBean OODBBean and removes it from cache. - * - * @param RedBean_OODBBean $bean bean - * - * @return mixed - */ - public function trash( $bean ) - { - $type = $bean->getMeta( 'type' ); - $id = $bean->id; - - if ( isset( $this->cache[$type][$id] ) ) { - unset( $this->cache[$type][$id] ); - } - - parent::trash( $bean ); - } - - /** - * Flushes the cache for a given type. - * - * @param string $type - * - * @return RedBean_Plugin_Cache - */ - public function flush( $type ) - { - if ( isset( $this->cache[$type] ) ) { - $this->cache[$type] = array(); - } - - return $this; - } - - /** - * Flushes the cache completely. - * - * @return RedBean_Plugin_Cache - */ - public function flushAll() - { - $this->cache = array(); - - return $this; - } - - /** - * Returns the number of hits. If a call to load() or - * batch() can use the cache this counts as a hit. - * Otherwise it's a miss. - * - * @return integer - */ - public function getHits() - { - return $this->hits; - } - - /** - * Returns the number of hits. If a call to load() or - * batch() can use the cache this counts as a hit. - * Otherwise it's a miss. - * - * @return integer - */ - public function getMisses() - { - return $this->misses; - } - - /** - * Resets hits counter to 0. - */ - public function resetHits() - { - $this->hits = 0; - } - - /** - * Resets misses counter to 0. - */ - public function resetMisses() - { - $this->misses = 0; - } -} - - -class RedBean_Plugin_TimeLine extends RedBean_Plugin_QueryLogger implements RedBean_Plugin { - /** - * Path to file to write SQL and comments to. - * - * @var string - */ - protected $file; - /** - * Constructor. - * Requires a path to an existing and writable file. - * - * @param string $outputPath path to file to write schema changes to. - */ - public function __construct($outputPath) { - if (!file_exists($outputPath) || !is_writable($outputPath)) - throw new RedBean_Exception_Security('Cannot write to file: '.$outputPath); - $this->file = $outputPath; - } - /** - * Implementation of the onEvent() method for Observer interface. - * If a query gets executed this method gets invoked because the - * adapter will send a signal to the attached logger. - * - * @param string $eventName ID of the event (name) - * @param RedBean_DBAdapter $adapter adapter that sends the signal - * - * @return void - */ - public function onEvent($eventName, $adapter) { - if ($eventName == 'sql_exec') { - $sql = $adapter->getSQL(); - $this->logs[] = $sql; - if (strpos($sql, 'ALTER') === 0) { - $write = "-- ".date('Y-m-d H:i')." | Altering table. \n"; - $write .= $sql; - $write .= "\n\n"; - } - if (strpos($sql, 'CREATE') === 0) { - $write = "-- ".date('Y-m-d H:i')." | Creating new table. \n"; - $write .= $sql; - $write .= "\n\n"; - } - if (isset($write)) { - file_put_contents($this->file, $write, FILE_APPEND); - } - } - } -} - -class RedBean_DependencyInjector -{ - - /** - * @var array - */ - protected $dependencies = array(); - - /** - * Adds a dependency to the list. - * You can add dependencies using this method. Pass both the key of the - * dependency and the dependency itself. The key of the dependency is a - * name that should match the setter. For instance if you have a dependency - * class called My_Mailer and a setter on the model called setMailSystem - * you should pass an instance of My_Mailer with key MailSystem. - * The injector will now look for a setter called setMailSystem. - * - * @param string $dependencyID name of the dependency (should match setter) - * @param mixed $dependency the service to be injected - * - * @return void - */ - public function addDependency( $dependencyID, $dependency ) - { - $this->dependencies[$dependencyID] = $dependency; - } - - /** - * Returns an instance of the class $modelClassName completely - * configured as far as possible with all the available - * service objects in the dependency list. - * - * @param string $modelClassName the name of the class of the model - * - * @return mixed - */ - public function getInstance( $modelClassName ) - { - $object = new $modelClassName; - - if ( $this->dependencies && is_array( $this->dependencies ) ) { - foreach ( $this->dependencies as $key => $dep ) { - $depSetter = 'set' . $key; - - if ( method_exists( $object, $depSetter ) ) { - $object->$depSetter( $dep ); - } - } - } - - return $object; - } -} - - -class RedBean_DuplicationManager -{ - - protected static $trees = false; - - /** - * @var RedBean_Toolbox + * @var ToolBox */ protected $toolbox; /** - * @var RedBean_AssociationManager + * @var AssociationManager */ protected $associationManager; /** - * @var RedBean_OODB + * @var OODB */ protected $redbean; @@ -12228,20 +10737,41 @@ class RedBean_DuplicationManager */ protected $cacheTables = FALSE; - public static function setAllowTrees($allow) { - self::$trees = $allow; + /** + * Recursively turns the keys of an array into + * camelCase. + * + * @param array $array array to camelize + * @param boolean $dolphinMode whether you want the exception for IDs. + * + * @return array + */ + public function camelfy( $array, $dolphinMode = false ) { + $newArray = array(); + foreach( $array as $key => $element ) { + $newKey = preg_replace_callback( '/_(\w)/', function( &$matches ){ + return strtoupper( $matches[1] ); + }, $key); + + if ( $dolphinMode ) { + $newKey = preg_replace( '/(\w)Id$/', '$1ID', $newKey ); + } + + $newArray[$newKey] = ( is_array($element) ) ? $this->camelfy( $element, $dolphinMode ) : $element; + } + return $newArray; } /** * Copies the shared beans in a bean, i.e. all the sharedBean-lists. * - * @param RedBean_OODBBean $copy target bean to copy lists to + * @param OODBBean $copy target bean to copy lists to * @param string $shared name of the shared list * @param array $beans array with shared beans to copy * * @return void */ - private function copySharedBeans( RedBean_OODBBean $copy, $shared, $beans ) + private function copySharedBeans( OODBBean $copy, $shared, $beans ) { $copy->$shared = array(); @@ -12255,7 +10785,7 @@ class RedBean_DuplicationManager * Each bean in the own-list belongs exclusively to its owner so * we need to invoke the duplicate method again to duplicate each bean here. * - * @param RedBean_OODBBean $copy target bean to copy lists to + * @param OODBBean $copy target bean to copy lists to * @param string $owned name of the own list * @param array $beans array with shared beans to copy * @param array $trail array with former beans to detect recursion @@ -12263,7 +10793,7 @@ class RedBean_DuplicationManager * * @return void */ - private function copyOwnBeans( RedBean_OODBBean $copy, $owned, $beans, $trail, $preserveIDs ) + private function copyOwnBeans( OODBBean $copy, $owned, $beans, $trail, $preserveIDs ) { $copy->$owned = array(); foreach ( $beans as $subBean ) { @@ -12276,16 +10806,17 @@ class RedBean_DuplicationManager * and the parents beans to the newly created bean. Also sets the ID of the bean * to 0. * - * @param RedBean_OODBBean $bean bean to copy + * @param OODBBean $bean bean to copy * - * @return RedBean_OODBBean + * @return OODBBean */ - private function createCopy( RedBean_OODBBean $bean ) + private function createCopy( OODBBean $bean ) { $type = $bean->getMeta( 'type' ); $copy = $this->redbean->dispense( $type ); - + $copy->setMeta( 'sys.dup-from-id', $bean->id ); + $copy->setMeta( 'sys.old-id', $bean->id ); $copy->importFrom( $bean ); $copy->id = 0; @@ -12298,11 +10829,11 @@ class RedBean_DuplicationManager * Returns TRUE if the bean occurs in the trail and FALSE otherwise. * * @param array $trail list of former beans - * @param RedBean_OODBBean $bean currently selected bean + * @param OODBBean $bean currently selected bean * * @return boolean */ - private function inTrailOrAdd( &$trail, RedBean_OODBBean $bean ) + private function inTrailOrAdd( &$trail, OODBBean $bean ) { $type = $bean->getMeta( 'type' ); $key = $type . $bean->getID(); @@ -12359,19 +10890,19 @@ class RedBean_DuplicationManager */ protected function hasSharedList( $type, $target ) { - return in_array( RedBean_QueryWriter_AQueryWriter::getAssocTableFormat( array( $type, $target ) ), $this->tables ); + return in_array( AQueryWriter::getAssocTableFormat( array( $type, $target ) ), $this->tables ); } /** - * @see RedBean_DuplicationManager::dup + * @see DuplicationManager::dup * - * @param RedBean_OODBBean $bean bean to be copied + * @param OODBBean $bean bean to be copied * @param array $trail trail to prevent infinite loops * @param boolean $preserveIDs preserve IDs * - * @return RedBean_OODBBean + * @return OODBBean */ - protected function duplicate( RedBean_OODBBean $bean, $trail = array(), $preserveIDs = FALSE ) + protected function duplicate( OODBBean $bean, $trail = array(), $preserveIDs = FALSE ) { if ( $this->inTrailOrAdd( $trail, $bean ) ) return $bean; @@ -12379,7 +10910,6 @@ class RedBean_DuplicationManager $copy = $this->createCopy( $bean ); foreach ( $this->tables as $table ) { - if ( !self::$trees && $table == $type ) continue; if ( !empty( $this->filters ) ) { if ( !in_array( $table, $this->filters ) ) continue; @@ -12411,9 +10941,9 @@ class RedBean_DuplicationManager * Constructor, * creates a new instance of DupManager. * - * @param RedBean_Toolbox $toolbox + * @param ToolBox $toolbox */ - public function __construct( RedBean_Toolbox $toolbox ) + public function __construct( ToolBox $toolbox ) { $this->toolbox = $toolbox; $this->redbean = $toolbox->getRedBean(); @@ -12503,13 +11033,13 @@ class RedBean_DuplicationManager * duplicate() that does all the work. This method takes care of creating a clone * of the bean to avoid the bean getting tainted (triggering saving when storing it). * - * @param RedBean_OODBBean $bean bean to be copied + * @param OODBBean $bean bean to be copied * @param array $trail for internal usage, pass array() * @param boolean $preserveIDs for internal usage * - * @return RedBean_OODBBean + * @return OODBBean */ - public function dup( RedBean_OODBBean $bean, $trail = array(), $preserveIDs = FALSE ) + public function dup( OODBBean $bean, $trail = array(), $preserveIDs = FALSE ) { if ( !count( $this->tables ) ) { $this->tables = $this->toolbox->getWriter()->getTables(); @@ -12528,7 +11058,7 @@ class RedBean_DuplicationManager $this->columns = array(); } - return $this->duplicate( $rs, $trail, $preserveIDs ); + return $rs; } /** @@ -12539,13 +11069,14 @@ class RedBean_DuplicationManager * - all own bean lists (recursively) * - all shared beans (not THEIR own lists) * - * @param array|RedBean_OODBBean $beans beans to be exported - * @param boolean $parents also export parents - * @param array $filters only these types (whitelist) + * @param array|OODBBean $beans beans to be exported + * @param boolean $parents also export parents + * @param array $filters only these types (whitelist) + * @param string $caseStyle case style identifier * * @return array */ - public function exportAll( $beans, $parents = FALSE, $filters = array() ) + public function exportAll( $beans, $parents = FALSE, $filters = array(), $caseStyle = 'snake') { $array = array(); @@ -12561,11 +11092,97 @@ class RedBean_DuplicationManager $array[] = $duplicate->export( FALSE, $parents, FALSE, $filters ); } + if ( $caseStyle === 'camel' ) $array = $this->camelfy( $array ); + if ( $caseStyle === 'dolphin' ) $array = $this->camelfy( $array, true ); + return $array; } } +} + +namespace RedBeanPHP { + +/** + * RedBean Plugin + * + * @file RedBean/Plugin.php + * @desc Marker interface for plugins. + * @author Gabor de Mooij and the RedBeanPHP Community + * @license BSD/GPLv2 + * + * copyright (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community + * This source file is subject to the BSD/GPLv2 License that is bundled + * with this source code in the file license.txt. + */ +interface Plugin +{ +} + +; +} + namespace { + + //make some classes available for backward compatibility + class RedBean_SimpleModel extends \RedBeanPHP\SimpleModel {}; + + if (!class_exists('R')) { + class R extends \RedBeanPHP\Facade{}; + } + + + +/** + * Support functions for RedBeanPHP. + * + * @file RedBeanPHP/Functions.php + * @desc Additional convenience shortcut functions for RedBeanPHP + * @author Gabor de Mooij and the RedBeanPHP community + * @license BSD/GPLv2 + * + * copyright (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community. + * This source file is subject to the BSD/GPLv2 License that is bundled + * with this source code in the file license.txt. + */ + +/** + * Convenience function for ENUM short syntax in queries. + * + * Usage: + * + * R::find( 'paint', ' color_id = ? ', [ EID('color:yellow') ] ); + * + * If a function called EID() already exists you'll have to write this + * wrapper yourself ;) + * + * @param string $enumName enum code as you would pass to R::enum() + * + * @return mixed + */ +if (!function_exists('EID')) { + + function EID($enumName) + { + return \RedBeanPHP\Facade::enum( $enumName )->id; + } + +} + +/** + * Prints the result of R::dump() to the screen using + * print_r. + * + * @param mixed $data data to dump + * + * @return void + */ +if ( !function_exists( 'dump' ) ) { + + function dmp( $list ) + { + print_r( \RedBeanPHP\Facade::dump( $list ) ); + } +} -class R extends RedBean_Facade{ - -} + } + \ No newline at end of file diff --git a/libs/Smarty.class.php b/libs/Smarty.class.php index 4411c72..4a53c36 100644 --- a/libs/Smarty.class.php +++ b/libs/Smarty.class.php @@ -2,33 +2,29 @@ /** * Project: Smarty: the PHP compiling template engine * File: Smarty.class.php - * SVN: $Id: Smarty.class.php 4800 2013-12-15 15:19:01Z Uwe.Tews@googlemail.com $ - * + * SVN: $Id: Smarty.class.php 4848 2014-06-08 18:12:09Z Uwe.Tews@googlemail.com $ * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. - * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * * For questions, help, comments, discussion, etc., please join the * Smarty mailing list. Send a blank e-mail to * smarty-discussion-subscribe@googlegroups.com * - * @link http://www.smarty.net/ + * @link http://www.smarty.net/ * @copyright 2008 New Digital Group, Inc. - * @author Monte Ohrt - * @author Uwe Tews - * @author Rodney Rehm - * @package Smarty - * @version 3.1-DEV + * @author Monte Ohrt + * @author Uwe Tews + * @author Rodney Rehm + * @package Smarty + * @version 3.1-DEV */ /** @@ -92,16 +88,17 @@ if (SMARTY_SPL_AUTOLOAD && set_include_path(get_include_path() . PATH_SEPARATOR /** * Load always needed external class files */ -include_once SMARTY_SYSPLUGINS_DIR.'smarty_internal_data.php'; -include_once SMARTY_SYSPLUGINS_DIR.'smarty_internal_templatebase.php'; -include_once SMARTY_SYSPLUGINS_DIR.'smarty_internal_template.php'; -include_once SMARTY_SYSPLUGINS_DIR.'smarty_resource.php'; -include_once SMARTY_SYSPLUGINS_DIR.'smarty_internal_resource_file.php'; -include_once SMARTY_SYSPLUGINS_DIR.'smarty_cacheresource.php'; -include_once SMARTY_SYSPLUGINS_DIR.'smarty_internal_cacheresource_file.php'; +include_once SMARTY_SYSPLUGINS_DIR . 'smarty_internal_data.php'; +include_once SMARTY_SYSPLUGINS_DIR . 'smarty_internal_templatebase.php'; +include_once SMARTY_SYSPLUGINS_DIR . 'smarty_internal_template.php'; +include_once SMARTY_SYSPLUGINS_DIR . 'smarty_resource.php'; +include_once SMARTY_SYSPLUGINS_DIR . 'smarty_internal_resource_file.php'; +include_once SMARTY_SYSPLUGINS_DIR . 'smarty_cacheresource.php'; +include_once SMARTY_SYSPLUGINS_DIR . 'smarty_internal_cacheresource_file.php'; /** * This is the main Smarty class + * * @package Smarty */ class Smarty extends Smarty_Internal_TemplateBase @@ -113,7 +110,7 @@ class Smarty extends Smarty_Internal_TemplateBase /** * smarty version */ - const SMARTY_VERSION = 'Smarty-3.1.16'; + const SMARTY_VERSION = 'Smarty-3.1.20'; /** * define variable scopes @@ -131,7 +128,7 @@ class Smarty extends Smarty_Internal_TemplateBase /** * define constant for clearing cache files be saved expiration datees */ - const CLEAR_EXPIRED = -1; + const CLEAR_EXPIRED = - 1; /** * define compile check modes @@ -206,111 +203,133 @@ class Smarty extends Smarty_Internal_TemplateBase /** * auto literal on delimiters with whitspace + * * @var boolean */ public $auto_literal = true; /** * display error on not assigned variables + * * @var boolean */ public $error_unassigned = false; /** * look up relative filepaths in include_path + * * @var boolean */ public $use_include_path = false; /** * template directory + * * @var array */ private $template_dir = array(); /** * joined template directory string used in cache keys + * * @var string */ public $joined_template_dir = null; /** * joined config directory string used in cache keys + * * @var string */ public $joined_config_dir = null; /** * default template handler + * * @var callable */ public $default_template_handler_func = null; /** * default config handler + * * @var callable */ public $default_config_handler_func = null; /** * default plugin handler + * * @var callable */ public $default_plugin_handler_func = null; /** * compile directory + * * @var string */ private $compile_dir = null; /** * plugins directory + * * @var array */ private $plugins_dir = array(); /** * cache directory + * * @var string */ private $cache_dir = null; /** * config directory + * * @var array */ private $config_dir = array(); /** * force template compiling? + * * @var boolean */ public $force_compile = false; /** * check template for modifications? + * * @var boolean */ public $compile_check = true; /** * use sub dirs for compiled/cached files? + * * @var boolean */ public $use_sub_dirs = false; /** * allow ambiguous resources (that are made unique by the resource handler) + * * @var boolean */ public $allow_ambiguous_resources = false; /** * caching enabled + * * @var boolean */ public $caching = false; /** * merge compiled includes + * * @var boolean */ public $merge_compiled_includes = false; /** * template inheritance merge compiled includes + * * @var boolean - */ + */ public $inheritance_merge_compiled_includes = true; /** * cache lifetime in seconds + * * @var integer */ public $cache_lifetime = 3600; /** * force cache file creation + * * @var boolean */ public $force_cache = false; @@ -330,11 +349,13 @@ class Smarty extends Smarty_Internal_TemplateBase public $compile_id = null; /** * template left-delimiter + * * @var string */ public $left_delimiter = "{"; /** * template right-delimiter + * * @var string */ public $right_delimiter = "}"; @@ -343,7 +364,6 @@ class Smarty extends Smarty_Internal_TemplateBase */ /** * class name - * * This should be instance of Smarty_Security. * * @var string @@ -370,7 +390,6 @@ class Smarty extends Smarty_Internal_TemplateBase public $allow_php_templates = false; /** * Should compiled-templates be prevented from being called directly? - * * {@internal * Currently used by Smarty_Internal_Template only. * }} @@ -381,7 +400,6 @@ class Smarty extends Smarty_Internal_TemplateBase /**#@-*/ /** * debug mode - * * Setting this to true enables the debug-console. * * @var boolean @@ -393,12 +411,12 @@ class Smarty extends Smarty_Internal_TemplateBase *
  • NONE => no debugging control allowed
  • *
  • URL => enable debugging when SMARTY_DEBUG is found in the URL.
  • * + * * @var string */ public $debugging_ctrl = 'NONE'; /** * Name of debugging URL-param. - * * Only used when $debugging_ctrl is set to 'URL'. * The name of the URL-parameter that activates debugging. * @@ -407,16 +425,19 @@ class Smarty extends Smarty_Internal_TemplateBase public $smarty_debug_id = 'SMARTY_DEBUG'; /** * Path of debug template. + * * @var string */ public $debug_tpl = null; /** * When set, smarty uses this value as error_reporting-level. + * * @var int */ public $error_reporting = null; /** * Internal flag for getTags() + * * @var boolean */ public $get_used_tags = false; @@ -427,16 +448,19 @@ class Smarty extends Smarty_Internal_TemplateBase /** * Controls whether variables with the same name overwrite each other. + * * @var boolean */ public $config_overwrite = true; /** * Controls whether config values of on/true/yes and off/false/no get converted to boolean. + * * @var boolean */ public $config_booleanize = true; /** * Controls whether hidden config sections/vars are read from the file. + * * @var boolean */ public $config_read_hidden = false; @@ -449,16 +473,19 @@ class Smarty extends Smarty_Internal_TemplateBase /** * locking concurrent compiles + * * @var boolean */ public $compile_locking = true; /** * Controls whether cache resources should emply locking mechanism + * * @var boolean */ public $cache_locking = false; /** * seconds to wait for acquiring a lock before ignoring the write lock + * * @var float */ public $locking_timeout = 10; @@ -467,19 +494,19 @@ class Smarty extends Smarty_Internal_TemplateBase /** * global template functions + * * @var array */ public $template_functions = array(); /** * resource type used if none given - * * Must be an valid key of $registered_resources. + * * @var string */ public $default_resource_type = 'file'; /** * caching type - * * Must be an element of $cache_resource_types. * * @var string @@ -487,121 +514,145 @@ class Smarty extends Smarty_Internal_TemplateBase public $caching_type = 'file'; /** * internal config properties + * * @var array */ public $properties = array(); /** * config type + * * @var string */ public $default_config_type = 'file'; /** * cached template objects + * * @var array */ public $template_objects = array(); /** * check If-Modified-Since headers + * * @var boolean */ public $cache_modified_check = false; /** * registered plugins + * * @var array */ public $registered_plugins = array(); /** * plugin search order + * * @var array */ public $plugin_search_order = array('function', 'block', 'compiler', 'class'); /** * registered objects + * * @var array */ public $registered_objects = array(); /** * registered classes + * * @var array */ public $registered_classes = array(); /** * registered filters + * * @var array */ public $registered_filters = array(); /** * registered resources + * * @var array */ public $registered_resources = array(); /** * resource handler cache + * * @var array */ public $_resource_handlers = array(); /** * registered cache resources + * * @var array */ public $registered_cache_resources = array(); /** * cache resource handler cache + * * @var array */ public $_cacheresource_handlers = array(); /** * autoload filter + * * @var array */ public $autoload_filters = array(); /** * default modifier + * * @var array */ public $default_modifiers = array(); /** * autoescape variable output + * * @var boolean */ public $escape_html = false; /** * global internal smarty vars + * * @var array */ public static $_smarty_vars = array(); /** * start time for execution time calculation + * * @var int */ public $start_time = 0; /** * default file permissions + * * @var int */ public $_file_perms = 0644; /** * default dir permissions + * * @var int */ public $_dir_perms = 0771; /** * block tag hierarchy + * * @var array */ public $_tag_stack = array(); /** * self pointer to Smarty object + * * @var Smarty */ public $smarty; /** * required by the compiler for BC + * * @var string */ public $_current_file = null; /** * internal flag to enable parser debugging + * * @var bool */ public $_parserdebug = false; @@ -615,7 +666,7 @@ class Smarty extends Smarty_Internal_TemplateBase /** * Initialize new Smarty object - * + */ public function __construct() { @@ -656,33 +707,32 @@ class Smarty extends Smarty_Internal_TemplateBase /** * <> Generic getter. - * * Calls the appropriate getter function. * Issues an E_USER_NOTICE if no valid getter is found. * * @param string $name property name + * * @return mixed */ public function __get($name) { $allowed = array( - 'template_dir' => 'getTemplateDir', - 'config_dir' => 'getConfigDir', - 'plugins_dir' => 'getPluginsDir', - 'compile_dir' => 'getCompileDir', - 'cache_dir' => 'getCacheDir', + 'template_dir' => 'getTemplateDir', + 'config_dir' => 'getConfigDir', + 'plugins_dir' => 'getPluginsDir', + 'compile_dir' => 'getCompileDir', + 'cache_dir' => 'getCacheDir', ); if (isset($allowed[$name])) { return $this->{$allowed[$name]}(); } else { - trigger_error('Undefined property: '. get_class($this) .'::$'. $name, E_USER_NOTICE); + trigger_error('Undefined property: ' . get_class($this) . '::$' . $name, E_USER_NOTICE); } } /** * <> Generic setter. - * * Calls the appropriate setter function. * Issues an E_USER_NOTICE if no valid setter is found. * @@ -692,11 +742,11 @@ class Smarty extends Smarty_Internal_TemplateBase public function __set($name, $value) { $allowed = array( - 'template_dir' => 'setTemplateDir', - 'config_dir' => 'setConfigDir', - 'plugins_dir' => 'setPluginsDir', - 'compile_dir' => 'setCompileDir', - 'cache_dir' => 'setCacheDir', + 'template_dir' => 'setTemplateDir', + 'config_dir' => 'setConfigDir', + 'plugins_dir' => 'setPluginsDir', + 'compile_dir' => 'setCompileDir', + 'cache_dir' => 'setCacheDir', ); if (isset($allowed[$name])) { @@ -709,7 +759,8 @@ class Smarty extends Smarty_Internal_TemplateBase /** * Check if a template resource exists * - * @param string $resource_name template name + * @param string $resource_name template name + * * @return boolean status */ public function templateExists($resource_name) @@ -727,8 +778,8 @@ class Smarty extends Smarty_Internal_TemplateBase /** * Returns a single or all global variables * - * @param object $smarty * @param string $varname variable name or null + * * @return string variable value or or array of variables */ public function getGlobal($varname = null) @@ -754,6 +805,7 @@ class Smarty extends Smarty_Internal_TemplateBase * * @param integer $exp_time expiration time * @param string $type resource type + * * @return integer number of cache files deleted */ public function clearAllCache($exp_time = null, $type = null) @@ -773,6 +825,7 @@ class Smarty extends Smarty_Internal_TemplateBase * @param string $compile_id compile id * @param integer $exp_time expiration time * @param string $type resource type + * * @return integer number of cache files deleted */ public function clearCache($template_name, $cache_id = null, $compile_id = null, $exp_time = null, $type = null) @@ -788,6 +841,7 @@ class Smarty extends Smarty_Internal_TemplateBase * Loads security class and enables security * * @param string|Smarty_Security $security_class if a string is used, it must be class-name + * * @return Smarty current Smarty instance for chaining * @throws SmartyException when an invalid class name is provided */ @@ -816,6 +870,7 @@ class Smarty extends Smarty_Internal_TemplateBase /** * Disable security + * * @return Smarty current Smarty instance for chaining */ public function disableSecurity() @@ -829,13 +884,14 @@ class Smarty extends Smarty_Internal_TemplateBase * Set template directory * * @param string|array $template_dir directory(s) of template sources + * * @return Smarty current Smarty instance for chaining */ public function setTemplateDir($template_dir) { $this->template_dir = array(); foreach ((array) $template_dir as $k => $v) { - $this->template_dir[$k] = rtrim($v, '/\\') . DS; + $this->template_dir[$k] = preg_replace('#(\w+)(/|\\\\){1,}#', '$1$2', rtrim($v, '/\\')) . DS; } $this->joined_template_dir = join(DIRECTORY_SEPARATOR, $this->template_dir); @@ -846,32 +902,37 @@ class Smarty extends Smarty_Internal_TemplateBase /** * Add template directory(s) * - * @param string|array $template_dir directory(s) of template sources - * @param string $key of the array element to assign the template dir to + * @param string|array $template_dir directory(s) of template sources + * @param string $key of the array element to assign the template dir to + * * @return Smarty current Smarty instance for chaining * @throws SmartyException when the given template directory is not valid */ - public function addTemplateDir($template_dir, $key=null) + public function addTemplateDir($template_dir, $key = null) { // make sure we're dealing with an array $this->template_dir = (array) $this->template_dir; if (is_array($template_dir)) { foreach ($template_dir as $k => $v) { + $v = preg_replace('#(\w+)(/|\\\\){1,}#', '$1$2', rtrim($v, '/\\')) . DS; if (is_int($k)) { // indexes are not merged but appended - $this->template_dir[] = rtrim($v, '/\\') . DS; + $this->template_dir[] = $v; } else { // string indexes are overridden - $this->template_dir[$k] = rtrim($v, '/\\') . DS; + $this->template_dir[$k] = $v; } } - } elseif ($key !== null) { - // override directory at specified index - $this->template_dir[$key] = rtrim($template_dir, '/\\') . DS; } else { - // append new directory - $this->template_dir[] = rtrim($template_dir, '/\\') . DS; + $v = preg_replace('#(\w+)(/|\\\\){1,}#', '$1$2', rtrim($template_dir, '/\\')) . DS; + if ($key !== null) { + // override directory at specified index + $this->template_dir[$key] = $v; + } else { + // append new directory + $this->template_dir[] = $v; + } } $this->joined_template_dir = join(DIRECTORY_SEPARATOR, $this->template_dir); @@ -881,10 +942,11 @@ class Smarty extends Smarty_Internal_TemplateBase /** * Get template directories * - * @param mixed index of directory to get, null to get all + * @param mixed $index index of directory to get, null to get all + * * @return array|string list of template directories, or directory of $index */ - public function getTemplateDir($index=null) + public function getTemplateDir($index = null) { if ($index !== null) { return isset($this->template_dir[$index]) ? $this->template_dir[$index] : null; @@ -896,14 +958,15 @@ class Smarty extends Smarty_Internal_TemplateBase /** * Set config directory * - * @param string|array $template_dir directory(s) of configuration sources + * @param $config_dir + * * @return Smarty current Smarty instance for chaining */ public function setConfigDir($config_dir) { $this->config_dir = array(); foreach ((array) $config_dir as $k => $v) { - $this->config_dir[$k] = rtrim($v, '/\\') . DS; + $this->config_dir[$k] = preg_replace('#(\w+)(/|\\\\){1,}#', '$1$2', rtrim($v, '/\\')) . DS; } $this->joined_config_dir = join(DIRECTORY_SEPARATOR, $this->config_dir); @@ -914,31 +977,36 @@ class Smarty extends Smarty_Internal_TemplateBase /** * Add config directory(s) * - * @param string|array $config_dir directory(s) of config sources - * @param string key of the array element to assign the config dir to + * @param string|array $config_dir directory(s) of config sources + * @param mixed $key key of the array element to assign the config dir to + * * @return Smarty current Smarty instance for chaining */ - public function addConfigDir($config_dir, $key=null) + public function addConfigDir($config_dir, $key = null) { // make sure we're dealing with an array $this->config_dir = (array) $this->config_dir; if (is_array($config_dir)) { foreach ($config_dir as $k => $v) { + $v = preg_replace('#(\w+)(/|\\\\){1,}#', '$1$2', rtrim($v, '/\\')) . DS; if (is_int($k)) { // indexes are not merged but appended - $this->config_dir[] = rtrim($v, '/\\') . DS; + $this->config_dir[] = $v; } else { // string indexes are overridden - $this->config_dir[$k] = rtrim($v, '/\\') . DS; + $this->config_dir[$k] = $v; } } - } elseif ($key !== null) { - // override directory at specified index - $this->config_dir[$key] = rtrim($config_dir, '/\\') . DS; } else { - // append new directory - $this->config_dir[] = rtrim($config_dir, '/\\') . DS; + $v = preg_replace('#(\w+)(/|\\\\){1,}#', '$1$2', rtrim($config_dir, '/\\')) . DS; + if ($key !== null) { + // override directory at specified index + $this->config_dir[$key] = rtrim($v, '/\\') . DS; + } else { + // append new directory + $this->config_dir[] = rtrim($v, '/\\') . DS; + } } $this->joined_config_dir = join(DIRECTORY_SEPARATOR, $this->config_dir); @@ -949,10 +1017,11 @@ class Smarty extends Smarty_Internal_TemplateBase /** * Get config directory * - * @param mixed index of directory to get, null to get all + * @param mixed $index index of directory to get, null to get all + * * @return array|string configuration directory */ - public function getConfigDir($index=null) + public function getConfigDir($index = null) { if ($index !== null) { return isset($this->config_dir[$index]) ? $this->config_dir[$index] : null; @@ -965,6 +1034,7 @@ class Smarty extends Smarty_Internal_TemplateBase * Set plugins directory * * @param string|array $plugins_dir directory(s) of plugins + * * @return Smarty current Smarty instance for chaining */ public function setPluginsDir($plugins_dir) @@ -980,8 +1050,8 @@ class Smarty extends Smarty_Internal_TemplateBase /** * Adds directory of plugin files * - * @param object $smarty - * @param string $ |array $ plugins folder + * @param $plugins_dir + * * @return Smarty current Smarty instance for chaining */ public function addPluginsDir($plugins_dir) @@ -1023,6 +1093,7 @@ class Smarty extends Smarty_Internal_TemplateBase * Set compile directory * * @param string $compile_dir directory to store compiled templates in + * * @return Smarty current Smarty instance for chaining */ public function setCompileDir($compile_dir) @@ -1049,6 +1120,7 @@ class Smarty extends Smarty_Internal_TemplateBase * Set cache directory * * @param string $cache_dir directory to store cached templates in + * * @return Smarty current Smarty instance for chaining */ public function setCacheDir($cache_dir) @@ -1075,6 +1147,7 @@ class Smarty extends Smarty_Internal_TemplateBase * Set default modifiers * * @param array|string $modifiers modifier or list of modifiers to set + * * @return Smarty current Smarty instance for chaining */ public function setDefaultModifiers($modifiers) @@ -1088,6 +1161,7 @@ class Smarty extends Smarty_Internal_TemplateBase * Add default modifiers * * @param array|string $modifiers modifier or list of modifiers to add + * * @return Smarty current Smarty instance for chaining */ public function addDefaultModifiers($modifiers) @@ -1111,15 +1185,15 @@ class Smarty extends Smarty_Internal_TemplateBase return $this->default_modifiers; } - /** * Set autoload filters * * @param array $filters filters to load automatically * @param string $type "pre", "output", … specify the filter type to set. Defaults to none treating $filters' keys as the appropriate types + * * @return Smarty current Smarty instance for chaining */ - public function setAutoloadFilters($filters, $type=null) + public function setAutoloadFilters($filters, $type = null) { if ($type !== null) { $this->autoload_filters[$type] = (array) $filters; @@ -1135,9 +1209,10 @@ class Smarty extends Smarty_Internal_TemplateBase * * @param array $filters filters to load automatically * @param string $type "pre", "output", … specify the filter type to set. Defaults to none treating $filters' keys as the appropriate types + * * @return Smarty current Smarty instance for chaining */ - public function addAutoloadFilters($filters, $type=null) + public function addAutoloadFilters($filters, $type = null) { if ($type !== null) { if (!empty($this->autoload_filters[$type])) { @@ -1162,9 +1237,10 @@ class Smarty extends Smarty_Internal_TemplateBase * Get autoload filters * * @param string $type type of filter to get autoloads for. Defaults to all autoload filters + * * @return array array( 'type1' => array( 'filter1', 'filter2', … ) ) or array( 'filter1', 'filter2', …) if $type was specified */ - public function getAutoloadFilters($type=null) + public function getAutoloadFilters($type = null) { if ($type !== null) { return isset($this->autoload_filters[$type]) ? $this->autoload_filters[$type] : array(); @@ -1186,7 +1262,8 @@ class Smarty extends Smarty_Internal_TemplateBase /** * set the debug template * - * @param string $tpl_name + * @param string $tpl_name + * * @return Smarty current Smarty instance for chaining * @throws SmartyException if file is not readable */ @@ -1208,15 +1285,16 @@ class Smarty extends Smarty_Internal_TemplateBase * @param mixed $compile_id compile id to be used with this template * @param object $parent next higher level of Smarty variables * @param boolean $do_clone flag is Smarty object shall be cloned + * * @return object template object */ public function createTemplate($template, $cache_id = null, $compile_id = null, $parent = null, $do_clone = true) { - if (!empty($cache_id) && (is_object($cache_id) || is_array($cache_id))) { + if ($cache_id !== null && (is_object($cache_id) || is_array($cache_id))) { $parent = $cache_id; $cache_id = null; } - if (!empty($parent) && is_array($parent)) { + if ($parent !== null && is_array($parent)) { $data = $parent; $parent = null; } else { @@ -1267,7 +1345,6 @@ class Smarty extends Smarty_Internal_TemplateBase return $tpl; } - /** * Takes unknown classes and loads plugin files for them * class name format: Smarty_PluginType_PluginName @@ -1275,6 +1352,8 @@ class Smarty extends Smarty_Internal_TemplateBase * * @param string $plugin_name class plugin name to load * @param bool $check check if already loaded + * + * @throws SmartyException * @return string |boolean filepath of loaded file or false */ public function loadPlugin($plugin_name, $check = true) @@ -1289,8 +1368,6 @@ class Smarty extends Smarty_Internal_TemplateBase // count($_name_parts) < 3 === !isset($_name_parts[2]) if (!isset($_name_parts[2]) || strtolower($_name_parts[0]) !== 'smarty') { throw new SmartyException("plugin {$plugin_name} is not a valid name format"); - - return false; } // if type is "internal", get plugin from sysplugins if (strtolower($_name_parts[1]) == 'internal') { @@ -1343,10 +1420,11 @@ class Smarty extends Smarty_Internal_TemplateBase /** * Compile all template files * - * @param string $extension file extension - * @param bool $force_compile force all to recompile - * @param int $time_limit - * @param int $max_errors + * @param string $extension file extension + * @param bool $force_compile force all to recompile + * @param int $time_limit + * @param int $max_errors + * * @return integer number of template files recompiled */ public function compileAllTemplates($extension = '.tpl', $force_compile = false, $time_limit = 0, $max_errors = null) @@ -1357,10 +1435,11 @@ class Smarty extends Smarty_Internal_TemplateBase /** * Compile all config files * - * @param string $extension file extension - * @param bool $force_compile force all to recompile - * @param int $time_limit - * @param int $max_errors + * @param string $extension file extension + * @param bool $force_compile force all to recompile + * @param int $time_limit + * @param int $max_errors + * * @return integer number of template files recompiled */ public function compileAllConfig($extension = '.conf', $force_compile = false, $time_limit = 0, $max_errors = null) @@ -1374,6 +1453,7 @@ class Smarty extends Smarty_Internal_TemplateBase * @param string $resource_name template name * @param string $compile_id compile id * @param integer $exp_time expiration time + * * @return integer number of template files deleted */ public function clearCompiledTemplate($resource_name = null, $compile_id = null, $exp_time = null) @@ -1381,11 +1461,11 @@ class Smarty extends Smarty_Internal_TemplateBase return Smarty_Internal_Utility::clearCompiledTemplate($resource_name, $compile_id, $exp_time, $this); } - /** * Return array of tag/attributes of all tags used by an template * - * @param object $templae template object + * @param Smarty_Internal_Template $template + * * @return array of tag/attributes */ public function getTags(Smarty_Internal_Template $template) @@ -1396,10 +1476,11 @@ class Smarty extends Smarty_Internal_TemplateBase /** * Run installation test * - * @param array $errors Array to write errors into, rather than outputting them + * @param array $errors Array to write errors into, rather than outputting them + * * @return boolean true if setup is fine, false if something is wrong */ - public function testInstall(&$errors=null) + public function testInstall(&$errors = null) { return Smarty_Internal_Utility::testInstall($this, $errors); } @@ -1408,7 +1489,13 @@ class Smarty extends Smarty_Internal_TemplateBase * Error Handler to mute expected messages * * @link http://php.net/set_error_handler + * * @param integer $errno Error level + * @param $errstr + * @param $errfile + * @param $errline + * @param $errcontext + * * @return boolean */ public static function mutingErrorHandler($errno, $errstr, $errfile, $errline, $errcontext) @@ -1420,7 +1507,7 @@ class Smarty extends Smarty_Internal_TemplateBase $smarty_dir = realpath(SMARTY_DIR); if ($smarty_dir !== false) { Smarty::$_muted_directories[SMARTY_DIR] = array( - 'file' => $smarty_dir, + 'file' => $smarty_dir, 'length' => strlen($smarty_dir), ); } @@ -1437,7 +1524,7 @@ class Smarty extends Smarty_Internal_TemplateBase continue; } $dir = array( - 'file' => $file, + 'file' => $file, 'length' => strlen($file), ); } @@ -1511,6 +1598,7 @@ if (Smarty::$_CHARSET !== 'UTF-8') { /** * Smarty exception class + * * @package Smarty */ class SmartyException extends Exception @@ -1519,12 +1607,13 @@ class SmartyException extends Exception public function __toString() { - return ' --> Smarty: ' . (self::$escape ? htmlentities($this->message) : $this->message) . ' <-- '; + return ' --> Smarty: ' . (self::$escape ? htmlentities($this->message) : $this->message) . ' <-- '; } } /** * Smarty compiler exception class + * * @package Smarty */ class SmartyCompilerException extends SmartyException @@ -1533,23 +1622,28 @@ class SmartyCompilerException extends SmartyException { return ' --> Smarty Compiler: ' . $this->message . ' <-- '; } + /** * The line number of the template error + * * @type int|null */ public $line = null; /** * The template source snippet relating to the error + * * @type string|null */ public $source = null; /** * The raw text of the error message + * * @type string|null */ public $desc = null; /** * The resource identifier or template name + * * @type string|null */ public $template = null; @@ -1562,16 +1656,16 @@ function smartyAutoload($class) { $_class = strtolower($class); static $_classes = array( - 'smarty_config_source' => true, - 'smarty_config_compiled' => true, - 'smarty_security' => true, - 'smarty_cacheresource' => true, - 'smarty_cacheresource_custom' => true, + 'smarty_config_source' => true, + 'smarty_config_compiled' => true, + 'smarty_security' => true, + 'smarty_cacheresource' => true, + 'smarty_cacheresource_custom' => true, 'smarty_cacheresource_keyvaluestore' => true, - 'smarty_resource' => true, - 'smarty_resource_custom' => true, - 'smarty_resource_uncompiled' => true, - 'smarty_resource_recompiled' => true, + 'smarty_resource' => true, + 'smarty_resource_custom' => true, + 'smarty_resource_uncompiled' => true, + 'smarty_resource_recompiled' => true, ); if (!strncmp($_class, 'smarty_internal_', 16) || isset($_classes[$_class])) { diff --git a/libs/SmartyBC.class.php b/libs/SmartyBC.class.php index 32f2228..cec9467 100644 --- a/libs/SmartyBC.class.php +++ b/libs/SmartyBC.class.php @@ -3,36 +3,32 @@ * Project: Smarty: the PHP compiling template engine * File: SmartyBC.class.php * SVN: $Id: $ - * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. - * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. - * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * * For questions, help, comments, discussion, etc., please join the * Smarty mailing list. Send a blank e-mail to * smarty-discussion-subscribe@googlegroups.com * - * @link http://www.smarty.net/ + * @link http://www.smarty.net/ * @copyright 2008 New Digital Group, Inc. - * @author Monte Ohrt - * @author Uwe Tews - * @author Rodney Rehm - * @package Smarty + * @author Monte Ohrt + * @author Uwe Tews + * @author Rodney Rehm + * @package Smarty */ /** * @ignore */ -require(dirname(__FILE__) . '/Smarty.class.php'); +require_once(dirname(__FILE__) . '/Smarty.class.php'); /** * Smarty Backward Compatability Wrapper Class @@ -43,6 +39,7 @@ class SmartyBC extends Smarty { /** * Smarty 2 BC + * * @var string */ public $_version = self::SMARTY_VERSION; @@ -52,7 +49,7 @@ class SmartyBC extends Smarty * * @param array $options options to set during initialization, e.g. array( 'forceCompile' => false ) */ - public function __construct(array $options=array()) + public function __construct(array $options = array()) { parent::__construct($options); // register {php} tag @@ -100,7 +97,7 @@ class SmartyBC extends Smarty * @param bool $cacheable * @param mixed $cache_attrs */ - public function register_function($function, $function_impl, $cacheable=true, $cache_attrs=null) + public function register_function($function, $function_impl, $cacheable = true, $cache_attrs = null) { $this->registerPlugin('function', $function, $function_impl, $cacheable, $cache_attrs); } @@ -118,11 +115,14 @@ class SmartyBC extends Smarty /** * Registers object to be used in templates * - * @param string $object name of template object - * @param object $object_impl the referenced PHP object to register - * @param array $allowed list of allowed methods (empty = all) - * @param boolean $smarty_args smarty argument format, else traditional - * @param array $block_functs list of methods that are block format + * @param string $object name of template object + * @param object $object_impl the referenced PHP object to register + * @param array $allowed list of allowed methods (empty = all) + * @param boolean $smarty_args smarty argument format, else traditional + * @param array $block_methods list of methods that are block format + * + * @throws SmartyException + * @internal param array $block_functs list of methods that are block format */ public function register_object($object, $object_impl, $allowed = array(), $smarty_args = true, $block_methods = array()) { @@ -144,12 +144,12 @@ class SmartyBC extends Smarty /** * Registers block function to be used in templates * - * @param string $block name of template block - * @param string $block_impl PHP function to register + * @param string $block name of template block + * @param string $block_impl PHP function to register * @param bool $cacheable * @param mixed $cache_attrs */ - public function register_block($block, $block_impl, $cacheable=true, $cache_attrs=null) + public function register_block($block, $block_impl, $cacheable = true, $cache_attrs = null) { $this->registerPlugin('block', $block, $block_impl, $cacheable, $cache_attrs); } @@ -171,7 +171,7 @@ class SmartyBC extends Smarty * @param string $function_impl name of PHP function to register * @param bool $cacheable */ - public function register_compiler_function($function, $function_impl, $cacheable=true) + public function register_compiler_function($function, $function_impl, $cacheable = true) { $this->registerPlugin('compiler', $function, $function_impl, $cacheable); } @@ -305,10 +305,11 @@ class SmartyBC extends Smarty /** * clear cached content for the given template and cache id * - * @param string $tpl_file name of template file - * @param string $cache_id name of cache_id - * @param string $compile_id name of compile_id - * @param string $exp_time expiration time + * @param string $tpl_file name of template file + * @param string $cache_id name of cache_id + * @param string $compile_id name of compile_id + * @param string $exp_time expiration time + * * @return boolean */ public function clear_cache($tpl_file = null, $cache_id = null, $compile_id = null, $exp_time = null) @@ -319,7 +320,8 @@ class SmartyBC extends Smarty /** * clear the entire contents of cache (all templates) * - * @param string $exp_time expire time + * @param string $exp_time expire time + * * @return boolean */ public function clear_all_cache($exp_time = null) @@ -330,9 +332,10 @@ class SmartyBC extends Smarty /** * test to see if valid cache exists for this template * - * @param string $tpl_file name of template file - * @param string $cache_id - * @param string $compile_id + * @param string $tpl_file name of template file + * @param string $cache_id + * @param string $compile_id + * * @return boolean */ public function is_cached($tpl_file, $cache_id = null, $compile_id = null) @@ -353,9 +356,10 @@ class SmartyBC extends Smarty * or all compiled template files if one is not specified. * This function is for advanced use only, not normally needed. * - * @param string $tpl_file - * @param string $compile_id - * @param string $exp_time + * @param string $tpl_file + * @param string $compile_id + * @param string $exp_time + * * @return boolean results of {@link smarty_core_rm_auto()} */ public function clear_compiled_tpl($tpl_file = null, $compile_id = null, $exp_time = null) @@ -366,7 +370,8 @@ class SmartyBC extends Smarty /** * Checks whether requested template exists. * - * @param string $tpl_file + * @param string $tpl_file + * * @return boolean */ public function template_exists($tpl_file) @@ -378,9 +383,10 @@ class SmartyBC extends Smarty * Returns an array containing template variables * * @param string $name + * * @return array */ - public function get_template_vars($name=null) + public function get_template_vars($name = null) { return $this->getTemplateVars($name); } @@ -389,9 +395,10 @@ class SmartyBC extends Smarty * Returns an array containing config variables * * @param string $name + * * @return array */ - public function get_config_vars($name=null) + public function get_config_vars($name = null) { return $this->getConfigVars($name); } @@ -412,6 +419,7 @@ class SmartyBC extends Smarty * return a reference to a registered object * * @param string $name + * * @return object */ public function get_registered_object($name) @@ -439,7 +447,6 @@ class SmartyBC extends Smarty { trigger_error("Smarty error: $error_msg", $error_type); } - } /** @@ -449,6 +456,7 @@ class SmartyBC extends Smarty * @param string $content contents of the block * @param object $template template object * @param boolean &$repeat repeat flag + * * @return string content re-formatted */ function smarty_php_tag($params, $content, $template, &$repeat) diff --git a/libs/debug.tpl b/libs/debug.tpl index 12eef0f..61b8876 100644 --- a/libs/debug.tpl +++ b/libs/debug.tpl @@ -1,133 +1,137 @@ {capture name='_smarty_debug' assign=debug_output} - - - - Smarty Debug Console - - - + #table_config_vars th { + color: maroon; + } -

    Smarty Debug Console - {if isset($template_name)}{$template_name|debug_print_var nofilter}{else}Total Time {$execution_time|string_format:"%.5f"}{/if}

    + {/literal} + + + -{if !empty($template_data)} -

    included templates & config files (load time in seconds)

    +

    Smarty Debug Console + - {if isset($template_name)}{$template_name|debug_print_var nofilter}{else}Total Time {$execution_time|string_format:"%.5f"}{/if}

    -
    -{foreach $template_data as $template} - {$template.name} - - (compile {$template['compile_time']|string_format:"%.5f"}) (render {$template['render_time']|string_format:"%.5f"}) (cache {$template['cache_time']|string_format:"%.5f"}) + {if !empty($template_data)} +

    included templates & config files (load time in seconds)

    +
    + {foreach $template_data as $template} + {$template.name} + + (compile {$template['compile_time']|string_format:"%.5f"}) (render {$template['render_time']|string_format:"%.5f"}) (cache {$template['cache_time']|string_format:"%.5f"} + ) -
    -{/foreach} -
    -{/if} +
    + {/foreach} +
    + {/if} -

    assigned template variables

    +

    assigned template variables

    -
    - {foreach $assigned_vars as $vars} - - - - {/foreach} -
    ${$vars@key|escape:'html'}{$vars|debug_print_var nofilter}
    + + {foreach $assigned_vars as $vars} + + + + + {/foreach} +
    ${$vars@key|escape:'html'}{$vars|debug_print_var nofilter}
    -

    assigned config file variables (outer template scope)

    +

    assigned config file variables (outer template scope)

    - - {foreach $config_vars as $vars} - - - - {/foreach} +
    {$vars@key|escape:'html'}{$vars|debug_print_var nofilter}
    + {foreach $config_vars as $vars} + + + + + {/foreach} -
    {$vars@key|escape:'html'}{$vars|debug_print_var nofilter}
    - - + + + {/capture} diff --git a/libs/plugins/block.textformat.php b/libs/plugins/block.textformat.php index b62a4f8..abf5449 100644 --- a/libs/plugins/block.textformat.php +++ b/libs/plugins/block.textformat.php @@ -2,13 +2,12 @@ /** * Smarty plugin to format text blocks * - * @package Smarty + * @package Smarty * @subpackage PluginsBlock */ /** * Smarty {textformat}{/textformat} block plugin - * * Type: block function
    * Name: textformat
    * Purpose: format text a certain way with preset styles @@ -23,12 +22,14 @@ * - wrap_boundary - boolean (true) * * - * @link http://www.smarty.net/manual/en/language.function.textformat.php {textformat} - * (Smarty online manual) + * @link http://www.smarty.net/manual/en/language.function.textformat.php {textformat} + * (Smarty online manual) + * * @param array $params parameters * @param string $content contents of the block * @param Smarty_Internal_Template $template template object * @param boolean &$repeat repeat flag + * * @return string content re-formatted * @author Monte Ohrt */ @@ -76,7 +77,6 @@ function smarty_block_textformat($params, $content, $template, &$repeat) } // split into paragraphs $_paragraphs = preg_split('![\r\n]{2}!', $content); - $_output = ''; foreach ($_paragraphs as &$_paragraph) { if (!$_paragraph) { diff --git a/libs/plugins/function.counter.php b/libs/plugins/function.counter.php index cd14763..4da85a1 100644 --- a/libs/plugins/function.counter.php +++ b/libs/plugins/function.counter.php @@ -1,22 +1,24 @@ * Name: counter
    * Purpose: print out a counter value * * @author Monte Ohrt - * @link http://www.smarty.net/manual/en/language.function.counter.php {counter} - * (Smarty online manual) + * @link http://www.smarty.net/manual/en/language.function.counter.php {counter} + * (Smarty online manual) + * * @param array $params parameters * @param Smarty_Internal_Template $template template object + * * @return string|null */ function smarty_function_counter($params, $template) @@ -26,11 +28,11 @@ function smarty_function_counter($params, $template) $name = (isset($params['name'])) ? $params['name'] : 'default'; if (!isset($counters[$name])) { $counters[$name] = array( - 'start'=>1, - 'skip'=>1, - 'direction'=>'up', - 'count'=>1 - ); + 'start' => 1, + 'skip' => 1, + 'direction' => 'up', + 'count' => 1 + ); } $counter =& $counters[$name]; @@ -66,11 +68,11 @@ function smarty_function_counter($params, $template) $counter['direction'] = $params['direction']; } - if ($counter['direction'] == "down") + if ($counter['direction'] == "down") { $counter['count'] -= $counter['skip']; - else + } else { $counter['count'] += $counter['skip']; + } return $retval; - } diff --git a/libs/plugins/function.cycle.php b/libs/plugins/function.cycle.php index 9bf26f8..8dc5cd9 100644 --- a/libs/plugins/function.cycle.php +++ b/libs/plugins/function.cycle.php @@ -2,13 +2,12 @@ /** * Smarty plugin * - * @package Smarty + * @package Smarty * @subpackage PluginsFunction */ /** * Smarty {cycle} function plugin - * * Type: function
    * Name: cycle
    * Date: May 3, 2002
    @@ -31,15 +30,17 @@ * {cycle name=row} * * - * @link http://www.smarty.net/manual/en/language.function.cycle.php {cycle} - * (Smarty online manual) - * @author Monte Ohrt - * @author credit to Mark Priatel - * @author credit to Gerard - * @author credit to Jason Sweat + * @link http://www.smarty.net/manual/en/language.function.cycle.php {cycle} + * (Smarty online manual) + * @author Monte Ohrt + * @author credit to Mark Priatel + * @author credit to Gerard + * @author credit to Jason Sweat * @version 1.3 + * * @param array $params parameters * @param Smarty_Internal_Template $template template object + * * @return string|null */ @@ -59,8 +60,9 @@ function smarty_function_cycle($params, $template) return; } } else { - if(isset($cycle_vars[$name]['values']) - && $cycle_vars[$name]['values'] != $params['values'] ) { + if (isset($cycle_vars[$name]['values']) + && $cycle_vars[$name]['values'] != $params['values'] + ) { $cycle_vars[$name]['index'] = 0; } $cycle_vars[$name]['values'] = $params['values']; @@ -75,10 +77,10 @@ function smarty_function_cycle($params, $template) if (is_array($cycle_vars[$name]['values'])) { $cycle_array = $cycle_vars[$name]['values']; } else { - $cycle_array = explode($cycle_vars[$name]['delimiter'],$cycle_vars[$name]['values']); + $cycle_array = explode($cycle_vars[$name]['delimiter'], $cycle_vars[$name]['values']); } - if (!isset($cycle_vars[$name]['index']) || $reset ) { + if (!isset($cycle_vars[$name]['index']) || $reset) { $cycle_vars[$name]['index'] = 0; } @@ -94,10 +96,10 @@ function smarty_function_cycle($params, $template) } if ($advance) { - if ( $cycle_vars[$name]['index'] >= count($cycle_array) -1 ) { + if ($cycle_vars[$name]['index'] >= count($cycle_array) - 1) { $cycle_vars[$name]['index'] = 0; } else { - $cycle_vars[$name]['index']++; + $cycle_vars[$name]['index'] ++; } } diff --git a/libs/plugins/function.fetch.php b/libs/plugins/function.fetch.php index a4eaef3..3506d4a 100644 --- a/libs/plugins/function.fetch.php +++ b/libs/plugins/function.fetch.php @@ -2,28 +2,30 @@ /** * Smarty plugin * - * @package Smarty + * @package Smarty * @subpackage PluginsFunction */ /** * Smarty {fetch} plugin - * * Type: function
    * Name: fetch
    * Purpose: fetch file, web or ftp data and display results * - * @link http://www.smarty.net/manual/en/language.function.fetch.php {fetch} - * (Smarty online manual) + * @link http://www.smarty.net/manual/en/language.function.fetch.php {fetch} + * (Smarty online manual) * @author Monte Ohrt + * * @param array $params parameters * @param Smarty_Internal_Template $template template object + * + * @throws SmartyException * @return string|null if the assign parameter is passed, Smarty assigns the result to a template variable */ function smarty_function_fetch($params, $template) { if (empty($params['file'])) { - trigger_error("[plugin] fetch parameter 'file' cannot be empty",E_USER_NOTICE); + trigger_error("[plugin] fetch parameter 'file' cannot be empty", E_USER_NOTICE); return; } @@ -60,7 +62,7 @@ function smarty_function_fetch($params, $template) $host = $server_name = $uri_parts['host']; $timeout = 30; $accept = "image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, */*"; - $agent = "Smarty Template Engine ". Smarty::SMARTY_VERSION; + $agent = "Smarty Template Engine " . Smarty::SMARTY_VERSION; $referer = ""; $uri = !empty($uri_parts['path']) ? $uri_parts['path'] : '/'; $uri .= !empty($uri_parts['query']) ? '?' . $uri_parts['query'] : ''; @@ -100,8 +102,8 @@ function smarty_function_fetch($params, $template) break; case "header": if (!empty($param_value)) { - if (!preg_match('![\w\d-]+: .+!',$param_value)) { - trigger_error("[plugin] invalid header format '".$param_value."'",E_USER_NOTICE); + if (!preg_match('![\w\d-]+: .+!', $param_value)) { + trigger_error("[plugin] invalid header format '" . $param_value . "'", E_USER_NOTICE); return; } else { @@ -118,7 +120,7 @@ function smarty_function_fetch($params, $template) if (!preg_match('!\D!', $param_value)) { $proxy_port = (int) $param_value; } else { - trigger_error("[plugin] invalid value for attribute '".$param_key."'",E_USER_NOTICE); + trigger_error("[plugin] invalid value for attribute '" . $param_key . "'", E_USER_NOTICE); return; } @@ -137,26 +139,26 @@ function smarty_function_fetch($params, $template) if (!preg_match('!\D!', $param_value)) { $timeout = (int) $param_value; } else { - trigger_error("[plugin] invalid value for attribute '".$param_key."'",E_USER_NOTICE); + trigger_error("[plugin] invalid value for attribute '" . $param_key . "'", E_USER_NOTICE); return; } break; default: - trigger_error("[plugin] unrecognized attribute '".$param_key."'",E_USER_NOTICE); + trigger_error("[plugin] unrecognized attribute '" . $param_key . "'", E_USER_NOTICE); return; } } if (!empty($proxy_host) && !empty($proxy_port)) { $_is_proxy = true; - $fp = fsockopen($proxy_host,$proxy_port,$errno,$errstr,$timeout); + $fp = fsockopen($proxy_host, $proxy_port, $errno, $errstr, $timeout); } else { - $fp = fsockopen($server_name,$port,$errno,$errstr,$timeout); + $fp = fsockopen($server_name, $port, $errno, $errstr, $timeout); } if (!$fp) { - trigger_error("[plugin] unable to fetch: $errstr ($errno)",E_USER_NOTICE); + trigger_error("[plugin] unable to fetch: $errstr ($errno)", E_USER_NOTICE); return; } else { @@ -179,35 +181,35 @@ function smarty_function_fetch($params, $template) } if (isset($extra_headers) && is_array($extra_headers)) { foreach ($extra_headers as $curr_header) { - fputs($fp, $curr_header."\r\n"); + fputs($fp, $curr_header . "\r\n"); } } if (!empty($user) && !empty($pass)) { - fputs($fp, "Authorization: BASIC ".base64_encode("$user:$pass")."\r\n"); + fputs($fp, "Authorization: BASIC " . base64_encode("$user:$pass") . "\r\n"); } fputs($fp, "\r\n"); while (!feof($fp)) { - $content .= fgets($fp,4096); + $content .= fgets($fp, 4096); } fclose($fp); - $csplit = preg_split("!\r\n\r\n!",$content,2); + $csplit = preg_split("!\r\n\r\n!", $content, 2); $content = $csplit[1]; if (!empty($params['assign_headers'])) { - $template->assign($params['assign_headers'],preg_split("!\r\n!",$csplit[0])); + $template->assign($params['assign_headers'], preg_split("!\r\n!", $csplit[0])); } } } else { - trigger_error("[plugin fetch] unable to parse URL, check syntax",E_USER_NOTICE); + trigger_error("[plugin fetch] unable to parse URL, check syntax", E_USER_NOTICE); return; } } else { $content = @file_get_contents($params['file']); if ($content === false) { - throw new SmartyException("{fetch} cannot read resource '" . $params['file'] ."'"); + throw new SmartyException("{fetch} cannot read resource '" . $params['file'] . "'"); } } diff --git a/libs/plugins/function.html_checkboxes.php b/libs/plugins/function.html_checkboxes.php index ad0e9cd..d786803 100644 --- a/libs/plugins/function.html_checkboxes.php +++ b/libs/plugins/function.html_checkboxes.php @@ -2,13 +2,12 @@ /** * Smarty plugin * - * @package Smarty + * @package Smarty * @subpackage PluginsFunction */ /** * Smarty {html_checkboxes} function plugin - * * File: function.html_checkboxes.php
    * Type: function
    * Name: html_checkboxes
    @@ -32,15 +31,17 @@ * - escape (optional) - escape the content (not value), defaults to true * * - * @link http://www.smarty.net/manual/en/language.function.html.checkboxes.php {html_checkboxes} - * (Smarty online manual) + * @link http://www.smarty.net/manual/en/language.function.html.checkboxes.php {html_checkboxes} + * (Smarty online manual) * @author Christopher Kvarme - * @author credits to Monte Ohrt + * @author credits to Monte Ohrt * @version 1.0 - * @param array $params parameters + * + * @param array $params parameters * @param object $template template object + * * @return string - * @uses smarty_function_escape_special_chars() + * @uses smarty_function_escape_special_chars() */ function smarty_function_html_checkboxes($params, $template) { @@ -89,7 +90,7 @@ function smarty_function_html_checkboxes($params, $template) if (method_exists($_sel, "__toString")) { $_sel = smarty_function_escape_special_chars((string) $_sel->__toString()); } else { - trigger_error("html_checkboxes: selected attribute contains an object of class '". get_class($_sel) ."' without __toString() method", E_USER_NOTICE); + trigger_error("html_checkboxes: selected attribute contains an object of class '" . get_class($_sel) . "' without __toString() method", E_USER_NOTICE); continue; } } else { @@ -101,7 +102,7 @@ function smarty_function_html_checkboxes($params, $template) if (method_exists($_val, "__toString")) { $selected = smarty_function_escape_special_chars((string) $_val->__toString()); } else { - trigger_error("html_checkboxes: selected attribute is an object of class '". get_class($_val) ."' without __toString() method", E_USER_NOTICE); + trigger_error("html_checkboxes: selected attribute is an object of class '" . get_class($_val) . "' without __toString() method", E_USER_NOTICE); } } else { $selected = smarty_function_escape_special_chars((string) $_val); @@ -116,7 +117,8 @@ function smarty_function_html_checkboxes($params, $template) case 'assign': break; - case 'strict': break; + case 'strict': + break; case 'disabled': case 'readonly': @@ -131,11 +133,11 @@ function smarty_function_html_checkboxes($params, $template) break; } - // omit break; to fall through! + // omit break; to fall through! default: if (!is_array($_val)) { - $extra .= ' '.$_key.'="'.smarty_function_escape_special_chars($_val).'"'; + $extra .= ' ' . $_key . '="' . smarty_function_escape_special_chars($_val) . '"'; } else { trigger_error("html_checkboxes: extra attribute '$_key' cannot be an array", E_USER_NOTICE); } @@ -143,17 +145,18 @@ function smarty_function_html_checkboxes($params, $template) } } - if (!isset($options) && !isset($values)) - return ''; /* raise error here? */ + if (!isset($options) && !isset($values)) { + return ''; + } /* raise error here? */ $_html_result = array(); if (isset($options)) { - foreach ($options as $_key=>$_val) { + foreach ($options as $_key => $_val) { $_html_result[] = smarty_function_html_checkboxes_output($name, $_key, $_val, $selected, $extra, $separator, $labels, $label_ids, $escape); } } else { - foreach ($values as $_i=>$_key) { + foreach ($values as $_i => $_key) { $_val = isset($output[$_i]) ? $output[$_i] : ''; $_html_result[] = smarty_function_html_checkboxes_output($name, $_key, $_val, $selected, $extra, $separator, $labels, $label_ids, $escape); } @@ -164,10 +167,9 @@ function smarty_function_html_checkboxes($params, $template) } else { return implode("\n", $_html_result); } - } -function smarty_function_html_checkboxes_output($name, $value, $output, $selected, $extra, $separator, $labels, $label_ids, $escape=true) +function smarty_function_html_checkboxes_output($name, $value, $output, $selected, $extra, $separator, $labels, $label_ids, $escape = true) { $_output = ''; @@ -175,7 +177,7 @@ function smarty_function_html_checkboxes_output($name, $value, $output, $selecte if (method_exists($value, "__toString")) { $value = (string) $value->__toString(); } else { - trigger_error("html_options: value is an object of class '". get_class($value) ."' without __toString() method", E_USER_NOTICE); + trigger_error("html_options: value is an object of class '" . get_class($value) . "' without __toString() method", E_USER_NOTICE); return ''; } @@ -187,7 +189,7 @@ function smarty_function_html_checkboxes_output($name, $value, $output, $selecte if (method_exists($output, "__toString")) { $output = (string) $output->__toString(); } else { - trigger_error("html_options: output is an object of class '". get_class($output) ."' without __toString() method", E_USER_NOTICE); + trigger_error("html_options: output is an object of class '" . get_class($output) . "' without __toString() method", E_USER_NOTICE); return ''; } @@ -229,7 +231,7 @@ function smarty_function_html_checkboxes_output($name, $value, $output, $selecte $_output .= ''; } - $_output .= $separator; + $_output .= $separator; return $_output; } diff --git a/libs/plugins/function.html_image.php b/libs/plugins/function.html_image.php index 1674a26..5037e8b 100644 --- a/libs/plugins/function.html_image.php +++ b/libs/plugins/function.html_image.php @@ -2,13 +2,12 @@ /** * Smarty plugin * - * @package Smarty + * @package Smarty * @subpackage PluginsFunction */ /** * Smarty {html_image} function plugin - * * Type: function
    * Name: html_image
    * Date: Feb 24, 2003
    @@ -24,15 +23,18 @@ * - path_prefix - prefix for path output (optional, default empty) * * - * @link http://www.smarty.net/manual/en/language.function.html.image.php {html_image} - * (Smarty online manual) - * @author Monte Ohrt - * @author credits to Duda + * @link http://www.smarty.net/manual/en/language.function.html.image.php {html_image} + * (Smarty online manual) + * @author Monte Ohrt + * @author credits to Duda * @version 1.0 + * * @param array $params parameters * @param Smarty_Internal_Template $template template object + * + * @throws SmartyException * @return string - * @uses smarty_function_escape_special_chars() + * @uses smarty_function_escape_special_chars() */ function smarty_function_html_image($params, $template) { @@ -112,7 +114,7 @@ function smarty_function_html_image($params, $template) } } else { // local file - if (!$template->smarty->security_policy->isTrustedResourceDir($params['file'])) { + if (!$template->smarty->security_policy->isTrustedResourceDir($_image_path)) { return; } } diff --git a/libs/plugins/function.html_options.php b/libs/plugins/function.html_options.php index 5ff9875..7ec3e06 100644 --- a/libs/plugins/function.html_options.php +++ b/libs/plugins/function.html_options.php @@ -2,13 +2,12 @@ /** * Smarty plugin * - * @package Smarty + * @package Smarty * @subpackage PluginsFunction */ /** * Smarty {html_options} function plugin - * * Type: function
    * Name: html_options
    * Purpose: Prints the list of ' . "\n"; - $idx++; + $idx ++; } else { $_idx = 0; - $_html_result = smarty_function_html_options_optgroup($key, $value, $selected, !empty($id) ? ($id.'-'.$idx) : null, $class, $_idx); - $idx++; + $_html_result = smarty_function_html_options_optgroup($key, $value, $selected, !empty($id) ? ($id . '-' . $idx) : null, $class, $_idx); + $idx ++; } return $_html_result; diff --git a/libs/plugins/function.html_radios.php b/libs/plugins/function.html_radios.php index 16606eb..f121d5e 100644 --- a/libs/plugins/function.html_radios.php +++ b/libs/plugins/function.html_radios.php @@ -2,13 +2,12 @@ /** * Smarty plugin * - * @package Smarty + * @package Smarty * @subpackage PluginsFunction */ /** * Smarty {html_radios} function plugin - * * File: function.html_radios.php
    * Type: function
    * Name: html_radios
    @@ -32,15 +31,17 @@ * {html_radios values=$ids checked=$checked separator='
    ' output=$names} * * - * @link http://smarty.php.net/manual/en/language.function.html.radios.php {html_radios} - * (Smarty online manual) - * @author Christopher Kvarme - * @author credits to Monte Ohrt + * @link http://smarty.php.net/manual/en/language.function.html.radios.php {html_radios} + * (Smarty online manual) + * @author Christopher Kvarme + * @author credits to Monte Ohrt * @version 1.0 + * * @param array $params parameters * @param Smarty_Internal_Template $template template object + * * @return string - * @uses smarty_function_escape_special_chars() + * @uses smarty_function_escape_special_chars() */ function smarty_function_html_radios($params, $template) { @@ -72,7 +73,7 @@ function smarty_function_html_radios($params, $template) if (method_exists($_val, "__toString")) { $selected = smarty_function_escape_special_chars((string) $_val->__toString()); } else { - trigger_error("html_radios: selected attribute is an object of class '". get_class($_val) ."' without __toString() method", E_USER_NOTICE); + trigger_error("html_radios: selected attribute is an object of class '" . get_class($_val) . "' without __toString() method", E_USER_NOTICE); } } else { $selected = (string) $_val; @@ -102,7 +103,8 @@ function smarty_function_html_radios($params, $template) case 'assign': break; - case 'strict': break; + case 'strict': + break; case 'disabled': case 'readonly': @@ -117,7 +119,7 @@ function smarty_function_html_radios($params, $template) break; } - // omit break; to fall through! + // omit break; to fall through! default: if (!is_array($_val)) { @@ -163,7 +165,7 @@ function smarty_function_html_radios_output($name, $value, $output, $selected, $ if (method_exists($value, "__toString")) { $value = (string) $value->__toString(); } else { - trigger_error("html_options: value is an object of class '". get_class($value) ."' without __toString() method", E_USER_NOTICE); + trigger_error("html_options: value is an object of class '" . get_class($value) . "' without __toString() method", E_USER_NOTICE); return ''; } @@ -175,7 +177,7 @@ function smarty_function_html_radios_output($name, $value, $output, $selected, $ if (method_exists($output, "__toString")) { $output = (string) $output->__toString(); } else { - trigger_error("html_options: output is an object of class '". get_class($output) ."' without __toString() method", E_USER_NOTICE); + trigger_error("html_options: output is an object of class '" . get_class($output) . "' without __toString() method", E_USER_NOTICE); return ''; } diff --git a/libs/plugins/function.html_select_date.php b/libs/plugins/function.html_select_date.php index b1bfee2..d662566 100644 --- a/libs/plugins/function.html_select_date.php +++ b/libs/plugins/function.html_select_date.php @@ -2,7 +2,7 @@ /** * Smarty plugin * - * @package Smarty + * @package Smarty * @subpackage PluginsFunction */ @@ -17,11 +17,9 @@ require_once(SMARTY_PLUGINS_DIR . 'shared.make_timestamp.php'); /** * Smarty {html_select_date} plugin - * * Type: function
    * Name: html_select_date
    * Purpose: Prints the dropdowns for date selection. - * * ChangeLog: *
      *            - 1.0 initial release
    @@ -41,17 +39,18 @@ require_once(SMARTY_PLUGINS_DIR . 'shared.make_timestamp.php');
      *              added attributes month_names, *_id
      * 
    * - * @link http://www.smarty.net/manual/en/language.function.html.select.date.php {html_select_date} - * (Smarty online manual) - * @version 2.0 - * @author Andrei Zmievski - * @author Monte Ohrt - * @author Rodney Rehm - * @param array $params parameters - * @param Smarty_Internal_Template $template template object + * @link http://www.smarty.net/manual/en/language.function.html.select.date.php {html_select_date} + * (Smarty online manual) + * @version 2.0 + * @author Andrei Zmievski + * @author Monte Ohrt + * @author Rodney Rehm + * + * @param array $params parameters + * * @return string */ -function smarty_function_html_select_date($params, $template) +function smarty_function_html_select_date($params) { // generate timestamps used for month names only static $_month_timestamps = null; @@ -59,7 +58,7 @@ function smarty_function_html_select_date($params, $template) if ($_month_timestamps === null) { $_current_year = date('Y'); $_month_timestamps = array(); - for ($i = 1; $i <= 12; $i++) { + for ($i = 1; $i <= 12; $i ++) { $_month_timestamps[$i] = mktime(0, 0, 0, $i, 1, 2000); } } @@ -181,22 +180,20 @@ function smarty_function_html_select_date($params, $template) if (isset($params['time']) && is_array($params['time'])) { if (isset($params['time'][$prefix . 'Year'])) { // $_REQUEST[$field_array] given - foreach (array('Y' => 'Year', 'm' => 'Month', 'd' => 'Day') as $_elementKey => $_elementName) { + foreach (array('Y' => 'Year', 'm' => 'Month', 'd' => 'Day') as $_elementKey => $_elementName) { $_variableName = '_' . strtolower($_elementName); $$_variableName = isset($params['time'][$prefix . $_elementName]) ? $params['time'][$prefix . $_elementName] : date($_elementKey); } - $time = mktime(0, 0, 0, $_month, $_day, $_year); } elseif (isset($params['time'][$field_array][$prefix . 'Year'])) { // $_REQUEST given - foreach (array('Y' => 'Year', 'm' => 'Month', 'd' => 'Day') as $_elementKey => $_elementName) { + foreach (array('Y' => 'Year', 'm' => 'Month', 'd' => 'Day') as $_elementKey => $_elementName) { $_variableName = '_' . strtolower($_elementName); $$_variableName = isset($params['time'][$field_array][$prefix . $_elementName]) ? $params['time'][$field_array][$prefix . $_elementName] : date($_elementKey); } - $time = mktime(0, 0, 0, $_month, $_day, $_year); } else { // no date found, use NOW list($_year, $_month, $_day) = $time = explode('-', date('Y-m-d')); @@ -219,9 +216,9 @@ function smarty_function_html_select_date($params, $template) if ($t === null) { $$key = (int) $_current_year; } elseif ($t[0] == '+') { - $$key = (int) ($_current_year + trim(substr($t, 1))); + $$key = (int) ($_current_year + (int)trim(substr($t, 1))); } elseif ($t[0] == '-') { - $$key = (int) ($_current_year - trim(substr($t, 1))); + $$key = (int) ($_current_year - (int)trim(substr($t, 1))); } else { $$key = (int) $$key; } @@ -236,7 +233,6 @@ function smarty_function_html_select_date($params, $template) // generate year if ($display_years) { - $_html_years = ''; $_extra = ''; $_name = $field_array ? ($field_array . '[' . $prefix . 'Year]') : ($prefix . 'Year'); if ($all_extra) { @@ -252,8 +248,8 @@ function smarty_function_html_select_date($params, $template) $_html_years = ' or if ($display_months) { - $_html_month = ''; $_extra = ''; $_name = $field_array ? ($field_array . '[' . $prefix . 'Month]') : ($prefix . 'Month'); if ($all_extra) { @@ -290,8 +285,8 @@ function smarty_function_html_select_date($params, $template) $_html_months = ' or if ($display_days) { - $_html_day = ''; $_extra = ''; $_name = $field_array ? ($field_array . '[' . $prefix . 'Day]') : ($prefix . 'Day'); if ($all_extra) { @@ -329,8 +323,8 @@ function smarty_function_html_select_date($params, $template) $_html_days = '' . $option_separator; if (isset($hour_empty) || isset($all_empty)) { - $_html_hours .= '' . $option_separator; + $_html_hours .= '' . $option_separator; } $start = $use_24_hours ? 0 : 1; $end = $use_24_hours ? 23 : 12; - for ($i=$start; $i <= $end; $i++) { + for ($i = $start; $i <= $end; $i ++) { $_val = sprintf('%02d', $i); $_text = $hour_format == '%02d' ? $_val : sprintf($hour_format, $i); $_value = $hour_value_format == '%02d' ? $_val : sprintf($hour_value_format, $i); @@ -226,7 +226,7 @@ function smarty_function_html_select_time($params, $template) if (!$use_24_hours) { $_hour12 = $_hour == 0 ? 12 - : ($_hour <= 12 ? $_hour : $_hour -12); + : ($_hour <= 12 ? $_hour : $_hour - 12); } $selected = $_hour !== null ? ($use_24_hours ? $_hour == $_val : $_hour12 == $_val) : null; @@ -253,8 +253,8 @@ function smarty_function_html_select_time($params, $template) $_html_minutes = '' . $option_separator; if (isset($second_empty) || isset($all_empty)) { - $_html_seconds .= '' . $option_separator; + $_html_seconds .= '' . $option_separator; } $selected = $_second !== null ? ($_second - $_second % $second_interval) : null; - for ($i=0; $i <= 59; $i += $second_interval) { + for ($i = 0; $i <= 59; $i += $second_interval) { $_val = sprintf('%02d', $i); $_text = $second_format == '%02d' ? $_val : sprintf($second_format, $i); $_value = $second_value_format == '%02d' ? $_val : sprintf($second_value_format, $i); @@ -333,8 +333,8 @@ function smarty_function_html_select_time($params, $template) $_html_meridian = ''; } diff --git a/libs/plugins/function.html_table.php b/libs/plugins/function.html_table.php index 275f6c2..ec7ba48 100644 --- a/libs/plugins/function.html_table.php +++ b/libs/plugins/function.html_table.php @@ -2,13 +2,12 @@ /** * Smarty plugin * - * @package Smarty + * @package Smarty * @subpackage PluginsFunction */ /** * Smarty {html_table} function plugin - * * Type: function
    * Name: html_table
    * Date: Feb 17, 2003
    @@ -37,17 +36,18 @@ * {table loop=$data cols="first,second,third" tr_attr=$colors} * * - * @author Monte Ohrt - * @author credit to Messju Mohr - * @author credit to boots - * @version 1.1 - * @link http://www.smarty.net/manual/en/language.function.html.table.php {html_table} - * (Smarty online manual) - * @param array $params parameters - * @param Smarty_Internal_Template $template template object + * @author Monte Ohrt + * @author credit to Messju Mohr + * @author credit to boots + * @version 1.1 + * @link http://www.smarty.net/manual/en/language.function.html.table.php {html_table} + * (Smarty online manual) + * + * @param array $params parameters + * * @return string */ -function smarty_function_html_table($params, $template) +function smarty_function_html_table($params) { $table_attr = 'border="1"'; $tr_attr = ''; @@ -63,7 +63,7 @@ function smarty_function_html_table($params, $template) $loop = null; if (!isset($params['loop'])) { - trigger_error("html_table: missing 'loop' parameter",E_USER_WARNING); + trigger_error("html_table: missing 'loop' parameter", E_USER_WARNING); return; } @@ -130,7 +130,7 @@ function smarty_function_html_table($params, $template) $cols = ($hdir == 'right') ? $cols : array_reverse($cols); $output .= "\n"; - for ($r = 0; $r < $cols_count; $r++) { + for ($r = 0; $r < $cols_count; $r ++) { $output .= ''; $output .= $cols[$r]; $output .= "\n"; @@ -139,12 +139,12 @@ function smarty_function_html_table($params, $template) } $output .= "\n"; - for ($r = 0; $r < $rows; $r++) { + for ($r = 0; $r < $rows; $r ++) { $output .= "\n"; - $rx = ($vdir == 'down') ? $r * $cols_count : ($rows-1 - $r) * $cols_count; + $rx = ($vdir == 'down') ? $r * $cols_count : ($rows - 1 - $r) * $cols_count; - for ($c = 0; $c < $cols_count; $c++) { - $x = ($hdir == 'right') ? $rx + $c : $rx + $cols_count-1 - $c; + for ($c = 0; $c < $cols_count; $c ++) { + $x = ($hdir == 'right') ? $rx + $c : $rx + $cols_count - 1 - $c; if ($inner != 'cols') { /* shuffle x to loop over rows*/ $x = floor($x / $cols_count) + ($x % $cols_count) * $rows; diff --git a/libs/plugins/function.mailto.php b/libs/plugins/function.mailto.php index ac94ae0..520fb7a 100644 --- a/libs/plugins/function.mailto.php +++ b/libs/plugins/function.mailto.php @@ -2,13 +2,12 @@ /** * Smarty plugin * - * @package Smarty + * @package Smarty * @subpackage PluginsFunction */ /** * Smarty {mailto} function plugin - * * Type: function
    * Name: mailto
    * Date: May 21, 2002 @@ -39,22 +38,23 @@ * {mailto address="me@domain.com" extra='class="mailto"'} * * - * @link http://www.smarty.net/manual/en/language.function.mailto.php {mailto} - * (Smarty online manual) - * @version 1.2 - * @author Monte Ohrt - * @author credits to Jason Sweat (added cc, bcc and subject functionality) - * @param array $params parameters - * @param Smarty_Internal_Template $template template object + * @link http://www.smarty.net/manual/en/language.function.mailto.php {mailto} + * (Smarty online manual) + * @version 1.2 + * @author Monte Ohrt + * @author credits to Jason Sweat (added cc, bcc and subject functionality) + * + * @param array $params parameters + * * @return string */ -function smarty_function_mailto($params, $template) +function smarty_function_mailto($params) { static $_allowed_encoding = array('javascript' => true, 'javascript_charcode' => true, 'hex' => true, 'none' => true); $extra = ''; if (empty($params['address'])) { - trigger_error("mailto: missing 'address' parameter",E_USER_WARNING); + trigger_error("mailto: missing 'address' parameter", E_USER_WARNING); return; } else { @@ -72,8 +72,9 @@ function smarty_function_mailto($params, $template) case 'cc': case 'bcc': case 'followupto': - if (!empty($value)) - $mail_parms[] = $var . '=' . str_replace($search, $replace, rawurlencode($value)); + if (!empty($value)) { + $mail_parms[] = $var . '=' . str_replace($search, $replace, rawurlencode($value)); + } break; case 'subject': @@ -104,7 +105,7 @@ function smarty_function_mailto($params, $template) $string = 'document.write(\'' . $text . '\');'; $js_encode = ''; - for ($x = 0, $_length = strlen($string); $x < $_length; $x++) { + for ($x = 0, $_length = strlen($string); $x < $_length; $x ++) { $js_encode .= '%' . bin2hex($string[$x]); } @@ -112,7 +113,7 @@ function smarty_function_mailto($params, $template) } elseif ($encode == 'javascript_charcode') { $string = '' . $text . ''; - for ($x = 0, $y = strlen($string); $x < $y; $x++) { + for ($x = 0, $y = strlen($string); $x < $y; $x ++) { $ord[] = ord($string[$x]); } @@ -127,12 +128,12 @@ function smarty_function_mailto($params, $template) } elseif ($encode == 'hex') { preg_match('!^(.*)(\?.*)$!', $address, $match); if (!empty($match[2])) { - trigger_error("mailto: hex encoding does not work with extra attributes. Try javascript.",E_USER_WARNING); + trigger_error("mailto: hex encoding does not work with extra attributes. Try javascript.", E_USER_WARNING); return; } $address_encode = ''; - for ($x = 0, $_length = strlen($address); $x < $_length; $x++) { + for ($x = 0, $_length = strlen($address); $x < $_length; $x ++) { if (preg_match('!\w!' . Smarty::$_UTF8_MODIFIER, $address[$x])) { $address_encode .= '%' . bin2hex($address[$x]); } else { @@ -140,7 +141,7 @@ function smarty_function_mailto($params, $template) } } $text_encode = ''; - for ($x = 0, $_length = strlen($text); $x < $_length; $x++) { + for ($x = 0, $_length = strlen($text); $x < $_length; $x ++) { $text_encode .= '&#x' . bin2hex($text[$x]) . ';'; } diff --git a/libs/plugins/function.math.php b/libs/plugins/function.math.php index 7bcc8ac..aba76e8 100644 --- a/libs/plugins/function.math.php +++ b/libs/plugins/function.math.php @@ -1,36 +1,37 @@ * Name: math
    * Purpose: handle math computations in template * - * @link http://www.smarty.net/manual/en/language.function.math.php {math} - * (Smarty online manual) + * @link http://www.smarty.net/manual/en/language.function.math.php {math} + * (Smarty online manual) * @author Monte Ohrt + * * @param array $params parameters * @param Smarty_Internal_Template $template template object + * * @return string|null */ function smarty_function_math($params, $template) { static $_allowed_funcs = array( - 'int' => true, 'abs' => true, 'ceil' => true, 'cos' => true, 'exp' => true, 'floor' => true, - 'log' => true, 'log10' => true, 'max' => true, 'min' => true, 'pi' => true, 'pow' => true, - 'rand' => true, 'round' => true, 'sin' => true, 'sqrt' => true, 'srand' => true ,'tan' => true + 'int' => true, 'abs' => true, 'ceil' => true, 'cos' => true, 'exp' => true, 'floor' => true, + 'log' => true, 'log10' => true, 'max' => true, 'min' => true, 'pi' => true, 'pow' => true, + 'rand' => true, 'round' => true, 'sin' => true, 'sqrt' => true, 'srand' => true, 'tan' => true ); // be sure equation parameter is present if (empty($params['equation'])) { - trigger_error("math: missing equation parameter",E_USER_WARNING); + trigger_error("math: missing equation parameter", E_USER_WARNING); return; } @@ -38,18 +39,18 @@ function smarty_function_math($params, $template) $equation = $params['equation']; // make sure parenthesis are balanced - if (substr_count($equation,"(") != substr_count($equation,")")) { - trigger_error("math: unbalanced parenthesis",E_USER_WARNING); + if (substr_count($equation, "(") != substr_count($equation, ")")) { + trigger_error("math: unbalanced parenthesis", E_USER_WARNING); return; } // match all vars in equation, make sure all are passed - preg_match_all("!(?:0x[a-fA-F0-9]+)|([a-zA-Z][a-zA-Z0-9_]*)!",$equation, $match); + preg_match_all("!(?:0x[a-fA-F0-9]+)|([a-zA-Z][a-zA-Z0-9_]*)!", $equation, $match); foreach ($match[1] as $curr_var) { if ($curr_var && !isset($params[$curr_var]) && !isset($_allowed_funcs[$curr_var])) { - trigger_error("math: function call $curr_var not allowed",E_USER_WARNING); + trigger_error("math: function call $curr_var not allowed", E_USER_WARNING); return; } @@ -58,13 +59,13 @@ function smarty_function_math($params, $template) foreach ($params as $key => $val) { if ($key != "equation" && $key != "format" && $key != "assign") { // make sure value is not empty - if (strlen($val)==0) { - trigger_error("math: parameter $key is empty",E_USER_WARNING); + if (strlen($val) == 0) { + trigger_error("math: parameter $key is empty", E_USER_WARNING); return; } if (!is_numeric($val)) { - trigger_error("math: parameter $key: is not numeric",E_USER_WARNING); + trigger_error("math: parameter $key: is not numeric", E_USER_WARNING); return; } @@ -72,19 +73,19 @@ function smarty_function_math($params, $template) } } $smarty_math_result = null; - eval("\$smarty_math_result = ".$equation.";"); + eval("\$smarty_math_result = " . $equation . ";"); if (empty($params['format'])) { if (empty($params['assign'])) { return $smarty_math_result; } else { - $template->assign($params['assign'],$smarty_math_result); + $template->assign($params['assign'], $smarty_math_result); } } else { if (empty($params['assign'])) { - printf($params['format'],$smarty_math_result); + printf($params['format'], $smarty_math_result); } else { - $template->assign($params['assign'],sprintf($params['format'],$smarty_math_result)); + $template->assign($params['assign'], sprintf($params['format'], $smarty_math_result)); } } } diff --git a/libs/plugins/modifier.capitalize.php b/libs/plugins/modifier.capitalize.php index c019df5..a8ad763 100644 --- a/libs/plugins/modifier.capitalize.php +++ b/libs/plugins/modifier.capitalize.php @@ -2,22 +2,21 @@ /** * Smarty plugin * - * @package Smarty + * @package Smarty * @subpackage PluginsModifier */ /** * Smarty capitalize modifier plugin - * * Type: modifier
    * Name: capitalize
    * Purpose: capitalize words in the string - * * {@internal {$string|capitalize:true:true} is the fastest option for MBString enabled systems }} * * @param string $string string to capitalize * @param boolean $uc_digits also capitalize "x123" to "X123" * @param boolean $lc_rest capitalize first letters, lowercase all following letters "aAa" to "Aaa" + * * @return string capitalized string * @author Monte Ohrt * @author Rodney Rehm @@ -27,40 +26,40 @@ function smarty_modifier_capitalize($string, $uc_digits = false, $lc_rest = fals if (Smarty::$_MBSTRING) { if ($lc_rest) { // uppercase (including hyphenated words) - $upper_string = mb_convert_case( $string, MB_CASE_TITLE, Smarty::$_CHARSET ); + $upper_string = mb_convert_case($string, MB_CASE_TITLE, Smarty::$_CHARSET); } else { // uppercase word breaks - $upper_string = preg_replace_callback("!(^|[^\p{L}'])([\p{Ll}])!S" . Smarty::$_UTF8_MODIFIER, 'smarty_mod_cap_mbconvert_cb', $string); + $upper_string = preg_replace_callback("!(^|[^\p{L}'])([\p{Ll}])!S" . Smarty::$_UTF8_MODIFIER, 'smarty_mod_cap_mbconvert_cb', $string); } // check uc_digits case if (!$uc_digits) { if (preg_match_all("!\b([\p{L}]*[\p{N}]+[\p{L}]*)\b!" . Smarty::$_UTF8_MODIFIER, $string, $matches, PREG_OFFSET_CAPTURE)) { - foreach($matches[1] as $match) { + foreach ($matches[1] as $match) { $upper_string = substr_replace($upper_string, mb_strtolower($match[0], Smarty::$_CHARSET), $match[1], strlen($match[0])); } - } + } } $upper_string = preg_replace_callback("!((^|\s)['\"])(\w)!" . Smarty::$_UTF8_MODIFIER, 'smarty_mod_cap_mbconvert2_cb', $upper_string); return $upper_string; } - + // lowercase first if ($lc_rest) { $string = strtolower($string); } // uppercase (including hyphenated words) - $upper_string = preg_replace_callback("!(^|[^\p{L}'])([\p{Ll}])!S" . Smarty::$_UTF8_MODIFIER, 'smarty_mod_cap_ucfirst_cb', $string); + $upper_string = preg_replace_callback("!(^|[^\p{L}'])([\p{Ll}])!S" . Smarty::$_UTF8_MODIFIER, 'smarty_mod_cap_ucfirst_cb', $string); // check uc_digits case if (!$uc_digits) { if (preg_match_all("!\b([\p{L}]*[\p{N}]+[\p{L}]*)\b!" . Smarty::$_UTF8_MODIFIER, $string, $matches, PREG_OFFSET_CAPTURE)) { - foreach($matches[1] as $match) { + foreach ($matches[1] as $match) { $upper_string = substr_replace($upper_string, strtolower($match[0]), $match[1], strlen($match[0])); } - } + } } $upper_string = preg_replace_callback("!((^|\s)['\"])(\w)!" . Smarty::$_UTF8_MODIFIER, 'smarty_mod_cap_ucfirst2_cb', $upper_string); return $upper_string; -} +} /* * @@ -70,18 +69,22 @@ function smarty_modifier_capitalize($string, $uc_digits = false, $lc_rest = fals * * @author Kyle Renfrow */ -function smarty_mod_cap_mbconvert_cb($matches){ - return stripslashes($matches[1]).mb_convert_case(stripslashes($matches[2]),MB_CASE_UPPER, Smarty::$_CHARSET); +function smarty_mod_cap_mbconvert_cb($matches) +{ + return stripslashes($matches[1]) . mb_convert_case(stripslashes($matches[2]), MB_CASE_UPPER, Smarty::$_CHARSET); } -function smarty_mod_cap_mbconvert2_cb($matches){ - return stripslashes($matches[1]).mb_convert_case(stripslashes($matches[3]),MB_CASE_UPPER, Smarty::$_CHARSET); +function smarty_mod_cap_mbconvert2_cb($matches) +{ + return stripslashes($matches[1]) . mb_convert_case(stripslashes($matches[3]), MB_CASE_UPPER, Smarty::$_CHARSET); } -function smarty_mod_cap_ucfirst_cb($matches){ - return stripslashes($matches[1]).ucfirst(stripslashes($matches[2])); +function smarty_mod_cap_ucfirst_cb($matches) +{ + return stripslashes($matches[1]) . ucfirst(stripslashes($matches[2])); } -function smarty_mod_cap_ucfirst2_cb($matches){ - return stripslashes($matches[1]).ucfirst(stripslashes($matches[3])); +function smarty_mod_cap_ucfirst2_cb($matches) +{ + return stripslashes($matches[1]) . ucfirst(stripslashes($matches[3])); } diff --git a/libs/plugins/modifier.date_format.php b/libs/plugins/modifier.date_format.php index e1a9d33..5ad7540 100644 --- a/libs/plugins/modifier.date_format.php +++ b/libs/plugins/modifier.date_format.php @@ -2,13 +2,12 @@ /** * Smarty plugin * - * @package Smarty + * @package Smarty * @subpackage PluginsModifier */ /** * Smarty date_format modifier plugin - * * Type: modifier
    * Name: date_format
    * Purpose: format datestamps via strftime
    @@ -17,23 +16,25 @@ * - format: strftime format for output * - default_date: default date if $string is empty * - * @link http://www.smarty.net/manual/en/language.modifier.date.format.php date_format (Smarty online manual) + * @link http://www.smarty.net/manual/en/language.modifier.date.format.php date_format (Smarty online manual) * @author Monte Ohrt + * * @param string $string input date string * @param string $format strftime format for output * @param string $default_date default date if $string is empty * @param string $formatter either 'strftime' or 'auto' + * * @return string |void - * @uses smarty_make_timestamp() + * @uses smarty_make_timestamp() */ -function smarty_modifier_date_format($string, $format=null, $default_date='', $formatter='auto') +function smarty_modifier_date_format($string, $format = null, $default_date = '', $formatter = 'auto') { if ($format === null) { $format = Smarty::$_DATE_FORMAT; } /** - * Include the {@link shared.make_timestamp.php} plugin - */ + * Include the {@link shared.make_timestamp.php} plugin + */ require_once(SMARTY_PLUGINS_DIR . 'shared.make_timestamp.php'); if ($string != '' && $string != '0000-00-00' && $string != '0000-00-00 00:00:00') { $timestamp = smarty_make_timestamp($string); @@ -42,7 +43,7 @@ function smarty_modifier_date_format($string, $format=null, $default_date='', $f } else { return; } - if ($formatter=='strftime'||($formatter=='auto'&&strpos($format,'%')!==false)) { + if ($formatter == 'strftime' || ($formatter == 'auto' && strpos($format, '%') !== false)) { if (DS == '\\') { $_win_from = array('%D', '%h', '%n', '%r', '%R', '%t', '%T'); $_win_to = array('%m/%d/%y', '%b', "\n", '%I:%M:%S %p', '%H:%M', "\t", '%H:%M:%S'); diff --git a/libs/plugins/modifier.debug_print_var.php b/libs/plugins/modifier.debug_print_var.php index ea63c8a..66363d2 100644 --- a/libs/plugins/modifier.debug_print_var.php +++ b/libs/plugins/modifier.debug_print_var.php @@ -2,38 +2,39 @@ /** * Smarty plugin * - * @package Smarty + * @package Smarty * @subpackage Debug */ /** * Smarty debug_print_var modifier plugin - * * Type: modifier
    * Name: debug_print_var
    * Purpose: formats variable contents for display in the console * * @author Monte Ohrt - * @param array|object $var variable to be formatted - * @param integer $depth maximum recursion depth if $var is an array - * @param integer $length maximum string length if $var is a string + * + * @param array|object $var variable to be formatted + * @param integer $depth maximum recursion depth if $var is an array + * @param integer $length maximum string length if $var is a string + * * @return string */ -function smarty_modifier_debug_print_var ($var, $depth = 0, $length = 40) +function smarty_modifier_debug_print_var($var, $depth = 0, $length = 40) { $_replace = array("\n" => '\n', - "\r" => '\r', - "\t" => '\t' - ); + "\r" => '\r', + "\t" => '\t' + ); switch (gettype($var)) { case 'array' : $results = 'Array (' . count($var) . ')'; foreach ($var as $curr_key => $curr_val) { $results .= '
    ' . str_repeat(' ', $depth * 2) - . '' . strtr($curr_key, $_replace) . ' => ' - . smarty_modifier_debug_print_var($curr_val, ++$depth, $length); - $depth--; + . '' . strtr($curr_key, $_replace) . ' => ' + . smarty_modifier_debug_print_var($curr_val, ++$depth, $length); + $depth --; } break; @@ -42,9 +43,9 @@ function smarty_modifier_debug_print_var ($var, $depth = 0, $length = 40) $results = '' . get_class($var) . ' Object (' . count($object_vars) . ')'; foreach ($object_vars as $curr_key => $curr_val) { $results .= '
    ' . str_repeat(' ', $depth * 2) - . ' ->' . strtr($curr_key, $_replace) . ' = ' - . smarty_modifier_debug_print_var($curr_val, ++$depth, $length); - $depth--; + . ' ->' . strtr($curr_key, $_replace) . ' = ' + . smarty_modifier_debug_print_var($curr_val, ++$depth, $length); + $depth --; } break; diff --git a/libs/plugins/modifier.escape.php b/libs/plugins/modifier.escape.php index bc8ad42..9fdb070 100644 --- a/libs/plugins/modifier.escape.php +++ b/libs/plugins/modifier.escape.php @@ -2,23 +2,24 @@ /** * Smarty plugin * - * @package Smarty + * @package Smarty * @subpackage PluginsModifier */ /** * Smarty escape modifier plugin - * * Type: modifier
    * Name: escape
    * Purpose: escape string for output * - * @link http://www.smarty.net/manual/en/language.modifier.count.characters.php count_characters (Smarty online manual) + * @link http://www.smarty.net/docs/en/language.modifier.escape * @author Monte Ohrt + * * @param string $string input string * @param string $esc_type escape type * @param string $char_set character set, used for htmlspecialchars() or htmlentities() * @param boolean $double_encode encode already encoded entitites again, used for htmlspecialchars() or htmlentities() + * * @return string escaped input string */ function smarty_modifier_escape($string, $esc_type = 'html', $char_set = null, $double_encode = true) @@ -105,7 +106,7 @@ function smarty_modifier_escape($string, $esc_type = 'html', $char_set = null, $ // Note that the UTF-8 encoded character ä will be represented as %c3%a4 $return = ''; $_length = strlen($string); - for ($x = 0; $x < $_length; $x++) { + for ($x = 0; $x < $_length; $x ++) { $return .= '%' . bin2hex($string[$x]); } @@ -124,7 +125,7 @@ function smarty_modifier_escape($string, $esc_type = 'html', $char_set = null, $ } // no MBString fallback $_length = strlen($string); - for ($x = 0; $x < $_length; $x++) { + for ($x = 0; $x < $_length; $x ++) { $return .= '&#x' . bin2hex($string[$x]) . ';'; } @@ -143,7 +144,7 @@ function smarty_modifier_escape($string, $esc_type = 'html', $char_set = null, $ } // no MBString fallback $_length = strlen($string); - for ($x = 0; $x < $_length; $x++) { + for ($x = 0; $x < $_length; $x ++) { $return .= '&#' . ord($string[$x]) . ';'; } @@ -179,7 +180,7 @@ function smarty_modifier_escape($string, $esc_type = 'html', $char_set = null, $ } $_length = strlen($string); - for ($_i = 0; $_i < $_length; $_i++) { + for ($_i = 0; $_i < $_length; $_i ++) { $_ord = ord(substr($string, $_i, 1)); // non-standard char, escape it if ($_ord >= 126) { diff --git a/libs/plugins/modifier.regex_replace.php b/libs/plugins/modifier.regex_replace.php index a44afb5..abb1ff5 100644 --- a/libs/plugins/modifier.regex_replace.php +++ b/libs/plugins/modifier.regex_replace.php @@ -2,23 +2,24 @@ /** * Smarty plugin * - * @package Smarty + * @package Smarty * @subpackage PluginsModifier */ /** * Smarty regex_replace modifier plugin - * * Type: modifier
    * Name: regex_replace
    * Purpose: regular expression search/replace * - * @link http://smarty.php.net/manual/en/language.modifier.regex.replace.php + * @link http://smarty.php.net/manual/en/language.modifier.regex.replace.php * regex_replace (Smarty online manual) - * @author Monte Ohrt - * @param string $string input string - * @param string|array $search regular expression(s) to search for - * @param string|array $replace string(s) that should be replaced + * @author Monte Ohrt + * + * @param string $string input string + * @param string|array $search regular expression(s) to search for + * @param string|array $replace string(s) that should be replaced + * * @return string */ function smarty_modifier_regex_replace($string, $search, $replace) @@ -36,6 +37,7 @@ function smarty_modifier_regex_replace($string, $search, $replace) /** * @param string $search string(s) that should be replaced + * * @return string * @ignore */ @@ -43,12 +45,12 @@ function _smarty_regex_replace_check($search) { // null-byte injection detection // anything behind the first null-byte is ignored - if (($pos = strpos($search,"\0")) !== false) { - $search = substr($search,0,$pos); + if (($pos = strpos($search, "\0")) !== false) { + $search = substr($search, 0, $pos); } // remove eval-modifier from $search if (preg_match('!([a-zA-Z\s]+)$!s', $search, $match) && (strpos($match[1], 'e') !== false)) { - $search = substr($search, 0, -strlen($match[1])) . preg_replace('![e\s]+!', '', $match[1]); + $search = substr($search, 0, - strlen($match[1])) . preg_replace('![e\s]+!', '', $match[1]); } return $search; diff --git a/libs/plugins/modifier.replace.php b/libs/plugins/modifier.replace.php index cf4f645..aa5e857 100644 --- a/libs/plugins/modifier.replace.php +++ b/libs/plugins/modifier.replace.php @@ -1,23 +1,25 @@ * Name: replace
    * Purpose: simple search/replace * - * @link http://smarty.php.net/manual/en/language.modifier.replace.php replace (Smarty online manual) + * @link http://smarty.php.net/manual/en/language.modifier.replace.php replace (Smarty online manual) * @author Monte Ohrt * @author Uwe Tews + * * @param string $string input string * @param string $search text to search for * @param string $replace replacement text + * * @return string */ function smarty_modifier_replace($string, $search, $replace) diff --git a/libs/plugins/modifier.spacify.php b/libs/plugins/modifier.spacify.php index 1a5f425..e5c41ad 100644 --- a/libs/plugins/modifier.spacify.php +++ b/libs/plugins/modifier.spacify.php @@ -1,25 +1,27 @@ * Name: spacify
    * Purpose: add spaces between characters in a string * - * @link http://smarty.php.net/manual/en/language.modifier.spacify.php spacify (Smarty online manual) + * @link http://smarty.php.net/manual/en/language.modifier.spacify.php spacify (Smarty online manual) * @author Monte Ohrt + * * @param string $string input string * @param string $spacify_char string to insert between characters. + * * @return string */ function smarty_modifier_spacify($string, $spacify_char = ' ') { // well… what about charsets besides latin and UTF-8? - return implode($spacify_char, preg_split('//' . Smarty::$_UTF8_MODIFIER, $string, -1, PREG_SPLIT_NO_EMPTY)); + return implode($spacify_char, preg_split('//' . Smarty::$_UTF8_MODIFIER, $string, - 1, PREG_SPLIT_NO_EMPTY)); } diff --git a/libs/plugins/modifier.truncate.php b/libs/plugins/modifier.truncate.php index e2810ed..fbe62e8 100644 --- a/libs/plugins/modifier.truncate.php +++ b/libs/plugins/modifier.truncate.php @@ -2,32 +2,34 @@ /** * Smarty plugin * - * @package Smarty + * @package Smarty * @subpackage PluginsModifier */ /** * Smarty truncate modifier plugin - * * Type: modifier
    * Name: truncate
    * Purpose: Truncate a string to a certain length if necessary, * optionally splitting in the middle of a word, and * appending the $etc string or inserting $etc into the middle. * - * @link http://smarty.php.net/manual/en/language.modifier.truncate.php truncate (Smarty online manual) + * @link http://smarty.php.net/manual/en/language.modifier.truncate.php truncate (Smarty online manual) * @author Monte Ohrt + * * @param string $string input string * @param integer $length length of truncated text * @param string $etc end string * @param boolean $break_words truncate at word boundary * @param boolean $middle truncate in the middle of text + * * @return string truncated string */ function smarty_modifier_truncate($string, $length = 80, $etc = '...', $break_words = false, $middle = false) { - if ($length == 0) + if ($length == 0) { return ''; + } if (Smarty::$_MBSTRING) { if (mb_strlen($string, Smarty::$_CHARSET) > $length) { diff --git a/libs/plugins/modifiercompiler.cat.php b/libs/plugins/modifiercompiler.cat.php index 5dff747..db9d81f 100644 --- a/libs/plugins/modifiercompiler.cat.php +++ b/libs/plugins/modifiercompiler.cat.php @@ -2,13 +2,12 @@ /** * Smarty plugin * - * @package Smarty + * @package Smarty * @subpackage PluginsModifierCompiler */ /** * Smarty cat modifier plugin - * * Type: modifier
    * Name: cat
    * Date: Feb 24, 2003
    @@ -16,13 +15,15 @@ * Input: string to catenate
    * Example: {$var|cat:"foo"} * - * @link http://smarty.php.net/manual/en/language.modifier.cat.php cat - * (Smarty online manual) + * @link http://smarty.php.net/manual/en/language.modifier.cat.php cat + * (Smarty online manual) * @author Uwe Tews + * * @param array $params parameters + * * @return string with compiled code */ -function smarty_modifiercompiler_cat($params, $compiler) +function smarty_modifiercompiler_cat($params) { - return '('.implode(').(', $params).')'; + return '(' . implode(').(', $params) . ')'; } diff --git a/libs/plugins/modifiercompiler.count_characters.php b/libs/plugins/modifiercompiler.count_characters.php index 778d038..f8463d8 100644 --- a/libs/plugins/modifiercompiler.count_characters.php +++ b/libs/plugins/modifiercompiler.count_characters.php @@ -2,23 +2,24 @@ /** * Smarty plugin * - * @package Smarty + * @package Smarty * @subpackage PluginsModifierCompiler */ /** * Smarty count_characters modifier plugin - * * Type: modifier
    * Name: count_characteres
    * Purpose: count the number of characters in a text * - * @link http://www.smarty.net/manual/en/language.modifier.count.characters.php count_characters (Smarty online manual) + * @link http://www.smarty.net/manual/en/language.modifier.count.characters.php count_characters (Smarty online manual) * @author Uwe Tews + * * @param array $params parameters + * * @return string with compiled code */ -function smarty_modifiercompiler_count_characters($params, $compiler) +function smarty_modifiercompiler_count_characters($params) { if (!isset($params[1]) || $params[1] != 'true') { return 'preg_match_all(\'/[^\s]/' . Smarty::$_UTF8_MODIFIER . '\',' . $params[0] . ', $tmp)'; diff --git a/libs/plugins/modifiercompiler.count_paragraphs.php b/libs/plugins/modifiercompiler.count_paragraphs.php index 581d291..34f0bbb 100644 --- a/libs/plugins/modifiercompiler.count_paragraphs.php +++ b/libs/plugins/modifiercompiler.count_paragraphs.php @@ -2,24 +2,25 @@ /** * Smarty plugin * - * @package Smarty + * @package Smarty * @subpackage PluginsModifierCompiler */ /** * Smarty count_paragraphs modifier plugin - * * Type: modifier
    * Name: count_paragraphs
    * Purpose: count the number of paragraphs in a text * - * @link http://www.smarty.net/manual/en/language.modifier.count.paragraphs.php + * @link http://www.smarty.net/manual/en/language.modifier.count.paragraphs.php * count_paragraphs (Smarty online manual) - * @author Uwe Tews + * @author Uwe Tews + * * @param array $params parameters + * * @return string with compiled code */ -function smarty_modifiercompiler_count_paragraphs($params, $compiler) +function smarty_modifiercompiler_count_paragraphs($params) { // count \r or \n characters return '(preg_match_all(\'#[\r\n]+#\', ' . $params[0] . ', $tmp)+1)'; diff --git a/libs/plugins/modifiercompiler.count_sentences.php b/libs/plugins/modifiercompiler.count_sentences.php index 5cb0f2a..f1ec560 100644 --- a/libs/plugins/modifiercompiler.count_sentences.php +++ b/libs/plugins/modifiercompiler.count_sentences.php @@ -2,24 +2,25 @@ /** * Smarty plugin * - * @package Smarty + * @package Smarty * @subpackage PluginsModifierCompiler */ /** * Smarty count_sentences modifier plugin - * * Type: modifier
    * Name: count_sentences * Purpose: count the number of sentences in a text * - * @link http://www.smarty.net/manual/en/language.modifier.count.paragraphs.php + * @link http://www.smarty.net/manual/en/language.modifier.count.paragraphs.php * count_sentences (Smarty online manual) - * @author Uwe Tews + * @author Uwe Tews + * * @param array $params parameters + * * @return string with compiled code */ -function smarty_modifiercompiler_count_sentences($params, $compiler) +function smarty_modifiercompiler_count_sentences($params) { // find periods, question marks, exclamation marks with a word before but not after. return 'preg_match_all("#\w[\.\?\!](\W|$)#S' . Smarty::$_UTF8_MODIFIER . '", ' . $params[0] . ', $tmp)'; diff --git a/libs/plugins/modifiercompiler.count_words.php b/libs/plugins/modifiercompiler.count_words.php index 9146900..8b4330f 100644 --- a/libs/plugins/modifiercompiler.count_words.php +++ b/libs/plugins/modifiercompiler.count_words.php @@ -2,23 +2,24 @@ /** * Smarty plugin * - * @package Smarty + * @package Smarty * @subpackage PluginsModifierCompiler */ /** * Smarty count_words modifier plugin - * * Type: modifier
    * Name: count_words
    * Purpose: count the number of words in a text * - * @link http://www.smarty.net/manual/en/language.modifier.count.words.php count_words (Smarty online manual) + * @link http://www.smarty.net/manual/en/language.modifier.count.words.php count_words (Smarty online manual) * @author Uwe Tews + * * @param array $params parameters + * * @return string with compiled code -*/ -function smarty_modifiercompiler_count_words($params, $compiler) + */ +function smarty_modifiercompiler_count_words($params) { if (Smarty::$_MBSTRING) { // return 'preg_match_all(\'#[\w\pL]+#' . Smarty::$_UTF8_MODIFIER . '\', ' . $params[0] . ', $tmp)'; diff --git a/libs/plugins/modifiercompiler.default.php b/libs/plugins/modifiercompiler.default.php index 83b3f36..fe77762 100644 --- a/libs/plugins/modifiercompiler.default.php +++ b/libs/plugins/modifiercompiler.default.php @@ -2,23 +2,24 @@ /** * Smarty plugin * - * @package Smarty + * @package Smarty * @subpackage PluginsModifierCompiler */ /** * Smarty default modifier plugin - * * Type: modifier
    * Name: default
    * Purpose: designate default value for empty variables * - * @link http://www.smarty.net/manual/en/language.modifier.default.php default (Smarty online manual) + * @link http://www.smarty.net/manual/en/language.modifier.default.php default (Smarty online manual) * @author Uwe Tews + * * @param array $params parameters + * * @return string with compiled code */ -function smarty_modifiercompiler_default ($params, $compiler) +function smarty_modifiercompiler_default($params) { $output = $params[0]; if (!isset($params[1])) { diff --git a/libs/plugins/modifiercompiler.escape.php b/libs/plugins/modifiercompiler.escape.php index 6c6a356..7e848aa 100644 --- a/libs/plugins/modifiercompiler.escape.php +++ b/libs/plugins/modifiercompiler.escape.php @@ -2,25 +2,27 @@ /** * Smarty plugin * - * @package Smarty + * @package Smarty * @subpackage PluginsModifierCompiler */ /** * @ignore */ -require_once( SMARTY_PLUGINS_DIR .'shared.literal_compiler_param.php' ); +require_once(SMARTY_PLUGINS_DIR . 'shared.literal_compiler_param.php'); /** * Smarty escape modifier plugin - * * Type: modifier
    * Name: escape
    * Purpose: escape string for output * - * @link http://www.smarty.net/docsv2/en/language.modifier.escape count_characters (Smarty online manual) + * @link http://www.smarty.net/docsv2/en/language.modifier.escape count_characters (Smarty online manual) * @author Rodney Rehm + * * @param array $params parameters + * @param $compiler + * * @return string with compiled code */ function smarty_modifiercompiler_escape($params, $compiler) @@ -43,13 +45,13 @@ function smarty_modifiercompiler_escape($params, $compiler) case 'html': if ($_double_encode) { return 'htmlspecialchars(' - . $params[0] .', ENT_QUOTES, ' - . var_export($char_set, true) . ', ' - . var_export($double_encode, true) . ')'; + . $params[0] . ', ENT_QUOTES, ' + . var_export($char_set, true) . ', ' + . var_export($double_encode, true) . ')'; } elseif ($double_encode) { return 'htmlspecialchars(' - . $params[0] .', ENT_QUOTES, ' - . var_export($char_set, true) . ')'; + . $params[0] . ', ENT_QUOTES, ' + . var_export($char_set, true) . ')'; } else { // fall back to modifier.escape.php } @@ -59,18 +61,18 @@ function smarty_modifiercompiler_escape($params, $compiler) if ($_double_encode) { // php >=5.2.3 - go native return 'mb_convert_encoding(htmlspecialchars(' - . $params[0] .', ENT_QUOTES, ' - . var_export($char_set, true) . ', ' - . var_export($double_encode, true) - . '), "HTML-ENTITIES", ' - . var_export($char_set, true) . ')'; + . $params[0] . ', ENT_QUOTES, ' + . var_export($char_set, true) . ', ' + . var_export($double_encode, true) + . '), "HTML-ENTITIES", ' + . var_export($char_set, true) . ')'; } elseif ($double_encode) { // php <5.2.3 - only handle double encoding return 'mb_convert_encoding(htmlspecialchars(' - . $params[0] .', ENT_QUOTES, ' - . var_export($char_set, true) - . '), "HTML-ENTITIES", ' - . var_export($char_set, true) . ')'; + . $params[0] . ', ENT_QUOTES, ' + . var_export($char_set, true) + . '), "HTML-ENTITIES", ' + . var_export($char_set, true) . ')'; } else { // fall back to modifier.escape.php } @@ -80,14 +82,14 @@ function smarty_modifiercompiler_escape($params, $compiler) if ($_double_encode) { // php >=5.2.3 - go native return 'htmlentities(' - . $params[0] .', ENT_QUOTES, ' - . var_export($char_set, true) . ', ' - . var_export($double_encode, true) . ')'; + . $params[0] . ', ENT_QUOTES, ' + . var_export($char_set, true) . ', ' + . var_export($double_encode, true) . ')'; } elseif ($double_encode) { // php <5.2.3 - only handle double encoding return 'htmlentities(' - . $params[0] .', ENT_QUOTES, ' - . var_export($char_set, true) . ')'; + . $params[0] . ', ENT_QUOTES, ' + . var_export($char_set, true) . ')'; } else { // fall back to modifier.escape.php } @@ -105,20 +107,20 @@ function smarty_modifiercompiler_escape($params, $compiler) case 'javascript': // escape quotes and backslashes, newlines, etc. return 'strtr(' . $params[0] . ', array("\\\\" => "\\\\\\\\", "\'" => "\\\\\'", "\"" => "\\\\\"", "\\r" => "\\\\r", "\\n" => "\\\n", " "<\/" ))'; - } - } catch (SmartyException $e) { + } + catch (SmartyException $e) { // pass through to regular plugin fallback } // could not optimize |escape call, so fallback to regular plugin if ($compiler->template->caching && ($compiler->tag_nocache | $compiler->nocache)) { - $compiler->template->required_plugins['nocache']['escape']['modifier']['file'] = SMARTY_PLUGINS_DIR .'modifier.escape.php'; + $compiler->template->required_plugins['nocache']['escape']['modifier']['file'] = SMARTY_PLUGINS_DIR . 'modifier.escape.php'; $compiler->template->required_plugins['nocache']['escape']['modifier']['function'] = 'smarty_modifier_escape'; } else { - $compiler->template->required_plugins['compiled']['escape']['modifier']['file'] = SMARTY_PLUGINS_DIR .'modifier.escape.php'; + $compiler->template->required_plugins['compiled']['escape']['modifier']['file'] = SMARTY_PLUGINS_DIR . 'modifier.escape.php'; $compiler->template->required_plugins['compiled']['escape']['modifier']['function'] = 'smarty_modifier_escape'; } - return 'smarty_modifier_escape(' . join( ', ', $params ) . ')'; + return 'smarty_modifier_escape(' . join(', ', $params) . ')'; } diff --git a/libs/plugins/modifiercompiler.from_charset.php b/libs/plugins/modifiercompiler.from_charset.php index 1dfee36..dab43e9 100644 --- a/libs/plugins/modifiercompiler.from_charset.php +++ b/libs/plugins/modifiercompiler.from_charset.php @@ -2,22 +2,23 @@ /** * Smarty plugin * - * @package Smarty + * @package Smarty * @subpackage PluginsModifierCompiler */ /** * Smarty from_charset modifier plugin - * * Type: modifier
    * Name: from_charset
    * Purpose: convert character encoding from $charset to internal encoding * * @author Rodney Rehm + * * @param array $params parameters + * * @return string with compiled code */ -function smarty_modifiercompiler_from_charset($params, $compiler) +function smarty_modifiercompiler_from_charset($params) { if (!Smarty::$_MBSTRING) { // FIXME: (rodneyrehm) shouldn't this throw an error? diff --git a/libs/plugins/modifiercompiler.indent.php b/libs/plugins/modifiercompiler.indent.php index 85bc610..e3ca208 100644 --- a/libs/plugins/modifiercompiler.indent.php +++ b/libs/plugins/modifiercompiler.indent.php @@ -1,24 +1,26 @@ * Name: indent
    * Purpose: indent lines of text * - * @link http://www.smarty.net/manual/en/language.modifier.indent.php indent (Smarty online manual) + * @link http://www.smarty.net/manual/en/language.modifier.indent.php indent (Smarty online manual) * @author Uwe Tews + * * @param array $params parameters + * * @return string with compiled code */ -function smarty_modifiercompiler_indent($params, $compiler) +function smarty_modifiercompiler_indent($params) { if (!isset($params[1])) { $params[1] = 4; diff --git a/libs/plugins/modifiercompiler.lower.php b/libs/plugins/modifiercompiler.lower.php index b1a2318..1d255f3 100644 --- a/libs/plugins/modifiercompiler.lower.php +++ b/libs/plugins/modifiercompiler.lower.php @@ -1,28 +1,30 @@ * Name: lower
    * Purpose: convert string to lowercase * - * @link http://www.smarty.net/manual/en/language.modifier.lower.php lower (Smarty online manual) + * @link http://www.smarty.net/manual/en/language.modifier.lower.php lower (Smarty online manual) * @author Monte Ohrt * @author Uwe Tews + * * @param array $params parameters + * * @return string with compiled code */ -function smarty_modifiercompiler_lower($params, $compiler) +function smarty_modifiercompiler_lower($params) { if (Smarty::$_MBSTRING) { - return 'mb_strtolower(' . $params[0] . ', \'' . addslashes(Smarty::$_CHARSET) . '\')' ; + return 'mb_strtolower(' . $params[0] . ', \'' . addslashes(Smarty::$_CHARSET) . '\')'; } // no MBString fallback return 'strtolower(' . $params[0] . ')'; diff --git a/libs/plugins/modifiercompiler.noprint.php b/libs/plugins/modifiercompiler.noprint.php index 71f6bfa..4906908 100644 --- a/libs/plugins/modifiercompiler.noprint.php +++ b/libs/plugins/modifiercompiler.noprint.php @@ -2,22 +2,20 @@ /** * Smarty plugin * - * @package Smarty + * @package Smarty * @subpackage PluginsModifierCompiler */ /** * Smarty noprint modifier plugin - * * Type: modifier
    * Name: noprint
    * Purpose: return an empty string * * @author Uwe Tews - * @param array $params parameters * @return string with compiled code */ -function smarty_modifiercompiler_noprint($params, $compiler) +function smarty_modifiercompiler_noprint() { return "''"; } diff --git a/libs/plugins/modifiercompiler.string_format.php b/libs/plugins/modifiercompiler.string_format.php index defbf6e..71cdf28 100644 --- a/libs/plugins/modifiercompiler.string_format.php +++ b/libs/plugins/modifiercompiler.string_format.php @@ -2,23 +2,24 @@ /** * Smarty plugin * - * @package Smarty + * @package Smarty * @subpackage PluginsModifierCompiler */ /** * Smarty string_format modifier plugin - * * Type: modifier
    * Name: string_format
    * Purpose: format strings via sprintf * - * @link http://www.smarty.net/manual/en/language.modifier.string.format.php string_format (Smarty online manual) + * @link http://www.smarty.net/manual/en/language.modifier.string.format.php string_format (Smarty online manual) * @author Uwe Tews + * * @param array $params parameters + * * @return string with compiled code */ -function smarty_modifiercompiler_string_format($params, $compiler) +function smarty_modifiercompiler_string_format($params) { return 'sprintf(' . $params[1] . ',' . $params[0] . ')'; } diff --git a/libs/plugins/modifiercompiler.strip.php b/libs/plugins/modifiercompiler.strip.php index 6c732e8..fcd6cba 100644 --- a/libs/plugins/modifiercompiler.strip.php +++ b/libs/plugins/modifiercompiler.strip.php @@ -2,13 +2,12 @@ /** * Smarty plugin * - * @package Smarty + * @package Smarty * @subpackage PluginsModifierCompiler */ /** * Smarty strip modifier plugin - * * Type: modifier
    * Name: strip
    * Purpose: Replace all repeated spaces, newlines, tabs @@ -16,13 +15,15 @@ * Example: {$var|strip} {$var|strip:" "}
    * Date: September 25th, 2002 * - * @link http://www.smarty.net/manual/en/language.modifier.strip.php strip (Smarty online manual) + * @link http://www.smarty.net/manual/en/language.modifier.strip.php strip (Smarty online manual) * @author Uwe Tews + * * @param array $params parameters + * * @return string with compiled code */ -function smarty_modifiercompiler_strip($params, $compiler) +function smarty_modifiercompiler_strip($params) { if (!isset($params[1])) { $params[1] = "' '"; diff --git a/libs/plugins/modifiercompiler.strip_tags.php b/libs/plugins/modifiercompiler.strip_tags.php index bc5eab2..3e6e130 100644 --- a/libs/plugins/modifiercompiler.strip_tags.php +++ b/libs/plugins/modifiercompiler.strip_tags.php @@ -2,26 +2,27 @@ /** * Smarty plugin * - * @package Smarty + * @package Smarty * @subpackage PluginsModifierCompiler */ /** * Smarty strip_tags modifier plugin - * * Type: modifier
    * Name: strip_tags
    * Purpose: strip html tags from text * - * @link http://www.smarty.net/manual/en/language.modifier.strip.tags.php strip_tags (Smarty online manual) + * @link http://www.smarty.net/manual/en/language.modifier.strip.tags.php strip_tags (Smarty online manual) * @author Uwe Tews + * * @param array $params parameters + * * @return string with compiled code */ -function smarty_modifiercompiler_strip_tags($params, $compiler) +function smarty_modifiercompiler_strip_tags($params) { - if (!isset($params[1]) || $params[1] === true || trim($params[1],'"') == 'true') { - return "preg_replace('!<[^>]*?>!', ' ', {$params[0]})"; + if (!isset($params[1]) || $params[1] === true || trim($params[1], '"') == 'true') { + return "preg_replace('!<[^>]*?>!', ' ', {$params[0]})"; } else { return 'strip_tags(' . $params[0] . ')'; } diff --git a/libs/plugins/modifiercompiler.to_charset.php b/libs/plugins/modifiercompiler.to_charset.php index af75779..9122d8b 100644 --- a/libs/plugins/modifiercompiler.to_charset.php +++ b/libs/plugins/modifiercompiler.to_charset.php @@ -2,22 +2,23 @@ /** * Smarty plugin * - * @package Smarty + * @package Smarty * @subpackage PluginsModifierCompiler */ /** * Smarty to_charset modifier plugin - * * Type: modifier
    * Name: to_charset
    * Purpose: convert character encoding from internal encoding to $charset * * @author Rodney Rehm + * * @param array $params parameters + * * @return string with compiled code */ -function smarty_modifiercompiler_to_charset($params, $compiler) +function smarty_modifiercompiler_to_charset($params) { if (!Smarty::$_MBSTRING) { // FIXME: (rodneyrehm) shouldn't this throw an error? diff --git a/libs/plugins/modifiercompiler.unescape.php b/libs/plugins/modifiercompiler.unescape.php index 19474fc..3b17ea2 100644 --- a/libs/plugins/modifiercompiler.unescape.php +++ b/libs/plugins/modifiercompiler.unescape.php @@ -2,22 +2,23 @@ /** * Smarty plugin * - * @package Smarty + * @package Smarty * @subpackage PluginsModifierCompiler */ /** * Smarty unescape modifier plugin - * * Type: modifier
    * Name: unescape
    * Purpose: unescape html entities * * @author Rodney Rehm + * * @param array $params parameters + * * @return string with compiled code */ -function smarty_modifiercompiler_unescape($params, $compiler) +function smarty_modifiercompiler_unescape($params) { if (!isset($params[1])) { $params[1] = 'html'; diff --git a/libs/plugins/modifiercompiler.upper.php b/libs/plugins/modifiercompiler.upper.php index f10ed18..52ca4e8 100644 --- a/libs/plugins/modifiercompiler.upper.php +++ b/libs/plugins/modifiercompiler.upper.php @@ -2,26 +2,27 @@ /** * Smarty plugin * - * @package Smarty + * @package Smarty * @subpackage PluginsModifierCompiler */ /** * Smarty upper modifier plugin - * * Type: modifier
    * Name: lower
    * Purpose: convert string to uppercase * - * @link http://smarty.php.net/manual/en/language.modifier.upper.php lower (Smarty online manual) + * @link http://smarty.php.net/manual/en/language.modifier.upper.php lower (Smarty online manual) * @author Uwe Tews + * * @param array $params parameters + * * @return string with compiled code */ -function smarty_modifiercompiler_upper($params, $compiler) +function smarty_modifiercompiler_upper($params) { if (Smarty::$_MBSTRING) { - return 'mb_strtoupper(' . $params[0] . ', \'' . addslashes(Smarty::$_CHARSET) . '\')' ; + return 'mb_strtoupper(' . $params[0] . ', \'' . addslashes(Smarty::$_CHARSET) . '\')'; } // no MBString fallback return 'strtoupper(' . $params[0] . ')'; diff --git a/libs/plugins/modifiercompiler.wordwrap.php b/libs/plugins/modifiercompiler.wordwrap.php index 39312b4..2ad928e 100644 --- a/libs/plugins/modifiercompiler.wordwrap.php +++ b/libs/plugins/modifiercompiler.wordwrap.php @@ -2,20 +2,22 @@ /** * Smarty plugin * - * @package Smarty + * @package Smarty * @subpackage PluginsModifierCompiler */ /** * Smarty wordwrap modifier plugin - * * Type: modifier
    * Name: wordwrap
    * Purpose: wrap a string of text at a given length * - * @link http://smarty.php.net/manual/en/language.modifier.wordwrap.php wordwrap (Smarty online manual) + * @link http://smarty.php.net/manual/en/language.modifier.wordwrap.php wordwrap (Smarty online manual) * @author Uwe Tews + * * @param array $params parameters + * @param $compiler + * * @return string with compiled code */ function smarty_modifiercompiler_wordwrap($params, $compiler) @@ -31,11 +33,11 @@ function smarty_modifiercompiler_wordwrap($params, $compiler) } $function = 'wordwrap'; if (Smarty::$_MBSTRING) { - if ($compiler->template->caching && ($compiler->tag_nocache | $compiler->nocache)) { - $compiler->template->required_plugins['nocache']['wordwrap']['modifier']['file'] = SMARTY_PLUGINS_DIR .'shared.mb_wordwrap.php'; + if ($compiler->template->caching && ($compiler->tag_nocache | $compiler->nocache)) { + $compiler->template->required_plugins['nocache']['wordwrap']['modifier']['file'] = SMARTY_PLUGINS_DIR . 'shared.mb_wordwrap.php'; $compiler->template->required_plugins['nocache']['wordwrap']['modifier']['function'] = 'smarty_mb_wordwrap'; } else { - $compiler->template->required_plugins['compiled']['wordwrap']['modifier']['file'] = SMARTY_PLUGINS_DIR .'shared.mb_wordwrap.php'; + $compiler->template->required_plugins['compiled']['wordwrap']['modifier']['file'] = SMARTY_PLUGINS_DIR . 'shared.mb_wordwrap.php'; $compiler->template->required_plugins['compiled']['wordwrap']['modifier']['function'] = 'smarty_mb_wordwrap'; } $function = 'smarty_mb_wordwrap'; diff --git a/libs/plugins/outputfilter.trimwhitespace.php b/libs/plugins/outputfilter.trimwhitespace.php index 9da7f3d..62ab4e7 100644 --- a/libs/plugins/outputfilter.trimwhitespace.php +++ b/libs/plugins/outputfilter.trimwhitespace.php @@ -2,22 +2,22 @@ /** * Smarty plugin * - * @package Smarty + * @package Smarty * @subpackage PluginsFilter */ /** * Smarty trimwhitespace outputfilter plugin - * * Trim unnecessary whitespace from HTML markup. * * @author Rodney Rehm - * @param string $source input string - * @param Smarty_Internal_Template $smarty Smarty object + * + * @param string $source input string + * * @return string filtered output - * @todo substr_replace() is not overloaded by mbstring.func_overload - so this function might fail! + * @todo substr_replace() is not overloaded by mbstring.func_overload - so this function might fail! */ -function smarty_outputfilter_trimwhitespace($source, Smarty_Internal_Template $smarty) +function smarty_outputfilter_trimwhitespace($source) { $store = array(); $_store = 0; @@ -35,13 +35,13 @@ function smarty_outputfilter_trimwhitespace($source, Smarty_Internal_Template $s $source = substr_replace($source, $replace, $match[0][1] - $_offset, $_length); $_offset += $_length - strlen($replace); - $_store++; + $_store ++; } } // Strip all HTML-Comments // yes, even the ones in