PageRenderTime 84ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 1ms

/RedBeanORM/resources/rb.php

https://github.com/TitanKing/todoplugins
PHP | 8022 lines | 2904 code | 663 blank | 4455 comment | 437 complexity | d8476b9dd4d010c4446bbdca07f72226 MD5 | raw file
Possible License(s): GPL-3.0, GPL-2.0, LGPL-2.1, LGPL-3.0
  1. <?php /*
  2. .______.
  3. _______ ____ __| _/\_ |__ ____ _____ ____
  4. \_ __ \_/ __ \ / __ | | __ \_/ __ \\__ \ / \
  5. | | \/\ ___// /_/ | | \_\ \ ___/ / __ \| | \
  6. |__| \___ >____ | |___ /\___ >____ /___| /
  7. \/ \/ \/ \/ \/ \/
  8. RedBean Database Objects -
  9. Written by Gabor de Mooij (c) copyright 2009-2012
  10. RedBean is DUAL Licensed BSD and GPLv2. You may choose the license that fits
  11. best for your project.
  12. BSD/GPLv2 License
  13. Redistribution and use in source and binary forms, with or without
  14. modification, are permitted provided that the following conditions are met:
  15. * Redistributions of source code must retain the above copyright
  16. notice, this list of conditions and the following disclaimer.
  17. * Redistributions in binary form must reproduce the above copyright
  18. notice, this list of conditions and the following disclaimer in the
  19. documentation and/or other materials provided with the distribution.
  20. * Neither the name of RedBeanPHP nor the
  21. names of its contributors may be used to endorse or promote products
  22. derived from this software without specific prior written permission.
  23. THIS SOFTWARE IS PROVIDED BY GABOR DE MOOIJ ''AS IS'' AND ANY
  24. EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  25. WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  26. DISCLAIMED. IN NO EVENT SHALL GABOR DE MOOIJ BE LIABLE FOR ANY
  27. DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  28. (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  29. LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  30. ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  31. (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  32. SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  33. RedBeanPHP is Written by Gabor de Mooij (G.J.G.T de Mooij) Copyright (c) 2011.
  34. GPLv2 LICENSE
  35. GNU GENERAL PUBLIC LICENSE
  36. Version 2, June 1991
  37. Copyright (C) 1989, 1991 Free Software Foundation, Inc.
  38. 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  39. Everyone is permitted to copy and distribute verbatim copies
  40. of this license document, but changing it is not allowed.
  41. Preamble
  42. The licenses for most software are designed to take away your
  43. freedom to share and change it. By contrast, the GNU General Public
  44. License is intended to guarantee your freedom to share and change free
  45. software--to make sure the software is free for all its users. This
  46. General Public License applies to most of the Free Software
  47. Foundation's software and to any other program whose authors commit to
  48. using it. (Some other Free Software Foundation software is covered by
  49. the GNU Lesser General Public License instead.) You can apply it to
  50. your programs, too.
  51. When we speak of free software, we are referring to freedom, not
  52. price. Our General Public Licenses are designed to make sure that you
  53. have the freedom to distribute copies of free software (and charge for
  54. this service if you wish), that you receive source code or can get it
  55. if you want it, that you can change the software or use pieces of it
  56. in new free programs; and that you know you can do these things.
  57. To protect your rights, we need to make restrictions that forbid
  58. anyone to deny you these rights or to ask you to surrender the rights.
  59. These restrictions translate to certain responsibilities for you if you
  60. distribute copies of the software, or if you modify it.
  61. For example, if you distribute copies of such a program, whether
  62. gratis or for a fee, you must give the recipients all the rights that
  63. you have. You must make sure that they, too, receive or can get the
  64. source code. And you must show them these terms so they know their
  65. rights.
  66. We protect your rights with two steps: (1) copyright the software, and
  67. (2) offer you this license which gives you legal permission to copy,
  68. distribute and/or modify the software.
  69. Also, for each author's protection and ours, we want to make certain
  70. that everyone understands that there is no warranty for this free
  71. software. If the software is modified by someone else and passed on, we
  72. want its recipients to know that what they have is not the original, so
  73. that any problems introduced by others will not reflect on the original
  74. authors' reputations.
  75. Finally, any free program is threatened constantly by software
  76. patents. We wish to avoid the danger that redistributors of a free
  77. program will individually obtain patent licenses, in effect making the
  78. program proprietary. To prevent this, we have made it clear that any
  79. patent must be licensed for everyone's free use or not licensed at all.
  80. The precise terms and conditions for copying, distribution and
  81. modification follow.
  82. GNU GENERAL PUBLIC LICENSE
  83. TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
  84. 0. This License applies to any program or other work which contains
  85. a notice placed by the copyright holder saying it may be distributed
  86. under the terms of this General Public License. The "Program", below,
  87. refers to any such program or work, and a "work based on the Program"
  88. means either the Program or any derivative work under copyright law:
  89. that is to say, a work containing the Program or a portion of it,
  90. either verbatim or with modifications and/or translated into another
  91. language. (Hereinafter, translation is included without limitation in
  92. the term "modification".) Each licensee is addressed as "you".
  93. Activities other than copying, distribution and modification are not
  94. covered by this License; they are outside its scope. The act of
  95. running the Program is not restricted, and the output from the Program
  96. is covered only if its contents constitute a work based on the
  97. Program (independent of having been made by running the Program).
  98. Whether that is true depends on what the Program does.
  99. 1. You may copy and distribute verbatim copies of the Program's
  100. source code as you receive it, in any medium, provided that you
  101. conspicuously and appropriately publish on each copy an appropriate
  102. copyright notice and disclaimer of warranty; keep intact all the
  103. notices that refer to this License and to the absence of any warranty;
  104. and give any other recipients of the Program a copy of this License
  105. along with the Program.
  106. You may charge a fee for the physical act of transferring a copy, and
  107. you may at your option offer warranty protection in exchange for a fee.
  108. 2. You may modify your copy or copies of the Program or any portion
  109. of it, thus forming a work based on the Program, and copy and
  110. distribute such modifications or work under the terms of Section 1
  111. above, provided that you also meet all of these conditions:
  112. a) You must cause the modified files to carry prominent notices
  113. stating that you changed the files and the date of any change.
  114. b) You must cause any work that you distribute or publish, that in
  115. whole or in part contains or is derived from the Program or any
  116. part thereof, to be licensed as a whole at no charge to all third
  117. parties under the terms of this License.
  118. c) If the modified program normally reads commands interactively
  119. when run, you must cause it, when started running for such
  120. interactive use in the most ordinary way, to print or display an
  121. announcement including an appropriate copyright notice and a
  122. notice that there is no warranty (or else, saying that you provide
  123. a warranty) and that users may redistribute the program under
  124. these conditions, and telling the user how to view a copy of this
  125. License. (Exception: if the Program itself is interactive but
  126. does not normally print such an announcement, your work based on
  127. the Program is not required to print an announcement.)
  128. These requirements apply to the modified work as a whole. If
  129. identifiable sections of that work are not derived from the Program,
  130. and can be reasonably considered independent and separate works in
  131. themselves, then this License, and its terms, do not apply to those
  132. sections when you distribute them as separate works. But when you
  133. distribute the same sections as part of a whole which is a work based
  134. on the Program, the distribution of the whole must be on the terms of
  135. this License, whose permissions for other licensees extend to the
  136. entire whole, and thus to each and every part regardless of who wrote it.
  137. Thus, it is not the intent of this section to claim rights or contest
  138. your rights to work written entirely by you; rather, the intent is to
  139. exercise the right to control the distribution of derivative or
  140. collective works based on the Program.
  141. In addition, mere aggregation of another work not based on the Program
  142. with the Program (or with a work based on the Program) on a volume of
  143. a storage or distribution medium does not bring the other work under
  144. the scope of this License.
  145. 3. You may copy and distribute the Program (or a work based on it,
  146. under Section 2) in object code or executable form under the terms of
  147. Sections 1 and 2 above provided that you also do one of the following:
  148. a) Accompany it with the complete corresponding machine-readable
  149. source code, which must be distributed under the terms of Sections
  150. 1 and 2 above on a medium customarily used for software interchange; or,
  151. b) Accompany it with a written offer, valid for at least three
  152. years, to give any third party, for a charge no more than your
  153. cost of physically performing source distribution, a complete
  154. machine-readable copy of the corresponding source code, to be
  155. distributed under the terms of Sections 1 and 2 above on a medium
  156. customarily used for software interchange; or,
  157. c) Accompany it with the information you received as to the offer
  158. to distribute corresponding source code. (This alternative is
  159. allowed only for noncommercial distribution and only if you
  160. received the program in object code or executable form with such
  161. an offer, in accord with Subsection b above.)
  162. The source code for a work means the preferred form of the work for
  163. making modifications to it. For an executable work, complete source
  164. code means all the source code for all modules it contains, plus any
  165. associated interface definition files, plus the scripts used to
  166. control compilation and installation of the executable. However, as a
  167. special exception, the source code distributed need not include
  168. anything that is normally distributed (in either source or binary
  169. form) with the major components (compiler, kernel, and so on) of the
  170. operating system on which the executable runs, unless that component
  171. itself accompanies the executable.
  172. If distribution of executable or object code is made by offering
  173. access to copy from a designated place, then offering equivalent
  174. access to copy the source code from the same place counts as
  175. distribution of the source code, even though third parties are not
  176. compelled to copy the source along with the object code.
  177. 4. You may not copy, modify, sublicense, or distribute the Program
  178. except as expressly provided under this License. Any attempt
  179. otherwise to copy, modify, sublicense or distribute the Program is
  180. void, and will automatically terminate your rights under this License.
  181. However, parties who have received copies, or rights, from you under
  182. this License will not have their licenses terminated so long as such
  183. parties remain in full compliance.
  184. 5. You are not required to accept this License, since you have not
  185. signed it. However, nothing else grants you permission to modify or
  186. distribute the Program or its derivative works. These actions are
  187. prohibited by law if you do not accept this License. Therefore, by
  188. modifying or distributing the Program (or any work based on the
  189. Program), you indicate your acceptance of this License to do so, and
  190. all its terms and conditions for copying, distributing or modifying
  191. the Program or works based on it.
  192. 6. Each time you redistribute the Program (or any work based on the
  193. Program), the recipient automatically receives a license from the
  194. original licensor to copy, distribute or modify the Program subject to
  195. these terms and conditions. You may not impose any further
  196. restrictions on the recipients' exercise of the rights granted herein.
  197. You are not responsible for enforcing compliance by third parties to
  198. this License.
  199. 7. If, as a consequence of a court judgment or allegation of patent
  200. infringement or for any other reason (not limited to patent issues),
  201. conditions are imposed on you (whether by court order, agreement or
  202. otherwise) that contradict the conditions of this License, they do not
  203. excuse you from the conditions of this License. If you cannot
  204. distribute so as to satisfy simultaneously your obligations under this
  205. License and any other pertinent obligations, then as a consequence you
  206. may not distribute the Program at all. For example, if a patent
  207. license would not permit royalty-free redistribution of the Program by
  208. all those who receive copies directly or indirectly through you, then
  209. the only way you could satisfy both it and this License would be to
  210. refrain entirely from distribution of the Program.
  211. If any portion of this section is held invalid or unenforceable under
  212. any particular circumstance, the balance of the section is intended to
  213. apply and the section as a whole is intended to apply in other
  214. circumstances.
  215. It is not the purpose of this section to induce you to infringe any
  216. patents or other property right claims or to contest validity of any
  217. such claims; this section has the sole purpose of protecting the
  218. integrity of the free software distribution system, which is
  219. implemented by public license practices. Many people have made
  220. generous contributions to the wide range of software distributed
  221. through that system in reliance on consistent application of that
  222. system; it is up to the author/donor to decide if he or she is willing
  223. to distribute software through any other system and a licensee cannot
  224. impose that choice.
  225. This section is intended to make thoroughly clear what is believed to
  226. be a consequence of the rest of this License.
  227. 8. If the distribution and/or use of the Program is restricted in
  228. certain countries either by patents or by copyrighted interfaces, the
  229. original copyright holder who places the Program under this License
  230. may add an explicit geographical distribution limitation excluding
  231. those countries, so that distribution is permitted only in or among
  232. countries not thus excluded. In such case, this License incorporates
  233. the limitation as if written in the body of this License.
  234. 9. The Free Software Foundation may publish revised and/or new versions
  235. of the General Public License from time to time. Such new versions will
  236. be similar in spirit to the present version, but may differ in detail to
  237. address new problems or concerns.
  238. Each version is given a distinguishing version number. If the Program
  239. specifies a version number of this License which applies to it and "any
  240. later version", you have the option of following the terms and conditions
  241. either of that version or of any later version published by the Free
  242. Software Foundation. If the Program does not specify a version number of
  243. this License, you may choose any version ever published by the Free Software
  244. Foundation.
  245. 10. If you wish to incorporate parts of the Program into other free
  246. programs whose distribution conditions are different, write to the author
  247. to ask for permission. For software which is copyrighted by the Free
  248. Software Foundation, write to the Free Software Foundation; we sometimes
  249. make exceptions for this. Our decision will be guided by the two goals
  250. of preserving the free status of all derivatives of our free software and
  251. of promoting the sharing and reuse of software generally.
  252. NO WARRANTY
  253. 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
  254. FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
  255. OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
  256. PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
  257. OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
  258. MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
  259. TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
  260. PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
  261. REPAIR OR CORRECTION.
  262. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
  263. WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
  264. REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
  265. INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
  266. OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
  267. TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
  268. YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
  269. PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
  270. POSSIBILITY OF SUCH DAMAGES.
  271. */
  272. /**
  273. * Interface for database drivers
  274. *
  275. * @file RedBean/Driver.php
  276. * @description Describes the API for database classes
  277. * The Driver API conforms to the ADODB pseudo standard
  278. * for database drivers.
  279. * @author Gabor de Mooij and the RedBeanPHP Community
  280. * @license BSD/GPLv2
  281. *
  282. *
  283. * copyright (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community
  284. * This source file is subject to the BSD/GPLv2 License that is bundled
  285. * with this source code in the file license.txt.
  286. */
  287. interface RedBean_Driver {
  288. /**
  289. * Runs a query and fetches results as a multi dimensional array.
  290. *
  291. * @param string $sql SQL to be executed
  292. *
  293. * @return array $results result
  294. */
  295. public function GetAll( $sql, $aValues=array() );
  296. /**
  297. * Runs a query and fetches results as a column.
  298. *
  299. * @param string $sql SQL Code to execute
  300. *
  301. * @return array $results Resultset
  302. */
  303. public function GetCol( $sql, $aValues=array() );
  304. /**
  305. * Runs a query an returns results as a single cell.
  306. *
  307. * @param string $sql SQL to execute
  308. *
  309. * @return mixed $cellvalue result cell
  310. */
  311. public function GetCell( $sql, $aValues=array() );
  312. /**
  313. * Runs a query and returns a flat array containing the values of
  314. * one row.
  315. *
  316. * @param string $sql SQL to execute
  317. *
  318. * @return array $row result row
  319. */
  320. public function GetRow( $sql, $aValues=array() );
  321. /**
  322. * Executes SQL code and allows key-value binding.
  323. * This function allows you to provide an array with values to bind
  324. * to query parameters. For instance you can bind values to question
  325. * marks in the query. Each value in the array corresponds to the
  326. * question mark in the query that matches the position of the value in the
  327. * array. You can also bind values using explicit keys, for instance
  328. * array(":key"=>123) will bind the integer 123 to the key :key in the
  329. * SQL. This method has no return value.
  330. *
  331. * @param string $sql SQL Code to execute
  332. * @param array $aValues Values to bind to SQL query
  333. *
  334. * @return void
  335. */
  336. public function Execute( $sql, $aValues=array() );
  337. /**
  338. * Escapes a string for use in SQL using the currently selected
  339. * driver driver.
  340. *
  341. * @param string $string string to be escaped
  342. *
  343. * @return string $string escaped string
  344. */
  345. public function Escape( $str );
  346. /**
  347. * Returns the latest insert ID if driver does support this
  348. * feature.
  349. *
  350. * @return integer $id primary key ID
  351. */
  352. public function GetInsertID();
  353. /**
  354. * Returns the number of rows affected by the most recent query
  355. * if the currently selected driver driver supports this feature.
  356. *
  357. * @return integer $numOfRows number of rows affected
  358. */
  359. public function Affected_Rows();
  360. /**
  361. * Toggles debug mode. In debug mode the driver will print all
  362. * SQL to the screen together with some information about the
  363. * results. All SQL code that passes through the driver will be
  364. * passes on to the screen for inspection.
  365. * This method has no return value.
  366. *
  367. * @param boolean $trueFalse turn on/off
  368. *
  369. * @return void
  370. */
  371. public function setDebugMode( $tf );
  372. /**
  373. * Starts a transaction.
  374. * This method is part of the transaction mechanism of
  375. * RedBeanPHP. All queries in a transaction are executed together.
  376. * In case of an error all commands will be rolled back so none of the
  377. * SQL in the transaction will affect the DB. Using transactions is
  378. * considered best practice.
  379. * This method has no return value.
  380. *
  381. * @return void
  382. */
  383. public function CommitTrans();
  384. /**
  385. * Commits a transaction.
  386. * This method is part of the transaction mechanism of
  387. * RedBeanPHP. All queries in a transaction are executed together.
  388. * In case of an error all commands will be rolled back so none of the
  389. * SQL in the transaction will affect the DB. Using transactions is
  390. * considered best practice.
  391. * This method has no return value.
  392. *
  393. * @return void
  394. */
  395. public function StartTrans();
  396. /**
  397. * Rolls back a transaction.
  398. * This method is part of the transaction mechanism of
  399. * RedBeanPHP. All queries in a transaction are executed together.
  400. * In case of an error all commands will be rolled back so none of the
  401. * SQL in the transaction will affect the DB. Using transactions is
  402. * considered best practice.
  403. * This method has no return value.
  404. *
  405. * @return void
  406. */
  407. public function FailTrans();
  408. }
  409. /**
  410. * PDO Driver
  411. * @file RedBean/PDO.php
  412. * @description PDO Driver
  413. * This Driver implements the RedBean Driver API
  414. * @author Gabor de Mooij and the RedBeanPHP Community, Desfrenes
  415. * @license BSD/GPLv2
  416. *
  417. *
  418. * (c) copyright Desfrenes & Gabor de Mooij and the RedBeanPHP community
  419. * This source file is subject to the BSD/GPLv2 License that is bundled
  420. * with this source code in the file license.txt.
  421. *
  422. */
  423. class RedBean_Driver_PDO implements RedBean_Driver {
  424. /**
  425. * Contains database DSN for connecting to database.
  426. * @var string
  427. */
  428. protected $dsn;
  429. /**
  430. * Whether we are in debugging mode or not.
  431. * @var boolean
  432. */
  433. protected $debug = false;
  434. /**
  435. * Holds an instance of ILogger implementation.
  436. * @var RedBean_ILogger
  437. */
  438. protected $logger = NULL;
  439. /**
  440. * Holds the PDO instance.
  441. * @var PDO
  442. */
  443. protected $pdo;
  444. /**
  445. * Holds integer number of affected rows from latest query
  446. * if driver supports this feature.
  447. * @var integer
  448. */
  449. protected $affected_rows;
  450. /**
  451. * Holds result resource.
  452. * @var integer
  453. */
  454. protected $rs;
  455. /**
  456. * Contains arbitrary connection data.
  457. * @var array
  458. */
  459. protected $connectInfo = array();
  460. /**
  461. * Whether you want to use classic String Only binding -
  462. * backward compatibility.
  463. * @var bool
  464. */
  465. public $flagUseStringOnlyBinding = false;
  466. /**
  467. * Whether we are currently connected or not.
  468. * This flag is being used to delay the connection until necessary.
  469. * Delaying connections is a good practice to speed up scripts that
  470. * don't need database connectivity but for some reason want to
  471. * init RedbeanPHP.
  472. * @var boolean
  473. */
  474. protected $isConnected = false;
  475. /**
  476. * Constructor. You may either specify dsn, user and password or
  477. * just give an existing PDO connection.
  478. * Examples:
  479. * $driver = new RedBean_Driver_PDO($dsn, $user, $password);
  480. * $driver = new RedBean_Driver_PDO($existingConnection);
  481. *
  482. * @param string|PDO $dsn database connection string
  483. * @param string $user optional
  484. * @param string $pass optional
  485. *
  486. * @return void
  487. */
  488. public function __construct($dsn, $user = null, $pass = null) {
  489. if ($dsn instanceof PDO) {
  490. $this->pdo = $dsn;
  491. $this->isConnected = true;
  492. $this->pdo->setAttribute(1002, 'SET NAMES utf8');
  493. $this->pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
  494. $this->pdo->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC);
  495. // make sure that the dsn at least contains the type
  496. $this->dsn = $this->getDatabaseType();
  497. } else {
  498. $this->dsn = $dsn;
  499. $this->connectInfo = array( 'pass'=>$pass, 'user'=>$user );
  500. }
  501. }
  502. /**
  503. * Establishes a connection to the database using PHP PDO
  504. * functionality. If a connection has already been established this
  505. * method will simply return directly. This method also turns on
  506. * UTF8 for the database and PDO-ERRMODE-EXCEPTION as well as
  507. * PDO-FETCH-ASSOC.
  508. *
  509. * @return void
  510. */
  511. public function connect() {
  512. if ($this->isConnected) return;
  513. $user = $this->connectInfo['user'];
  514. $pass = $this->connectInfo['pass'];
  515. //PDO::MYSQL_ATTR_INIT_COMMAND
  516. $this->pdo = new PDO(
  517. $this->dsn,
  518. $user,
  519. $pass,
  520. array(1002 => 'SET NAMES utf8',
  521. PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
  522. PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
  523. )
  524. );
  525. $this->pdo->setAttribute(PDO::ATTR_STRINGIFY_FETCHES, true);
  526. $this->isConnected = true;
  527. }
  528. /**
  529. * Binds parameters. This method binds parameters to a PDOStatement for
  530. * Query Execution. This method binds parameters as NULL, INTEGER or STRING
  531. * and supports both named keys and question mark keys.
  532. *
  533. * @param PDOStatement $s PDO Statement instance
  534. * @param array $aValues values that need to get bound to the statement
  535. *
  536. * @return void
  537. */
  538. protected function bindParams($s,$aValues) {
  539. foreach($aValues as $key=>&$value) {
  540. if (is_integer($key)) {
  541. if (is_null($value)){
  542. $s->bindValue($key+1,null,PDO::PARAM_NULL);
  543. }
  544. elseif (!$this->flagUseStringOnlyBinding && RedBean_QueryWriter_AQueryWriter::canBeTreatedAsInt($value) && $value < 2147483648) {
  545. $s->bindParam($key+1,$value,PDO::PARAM_INT);
  546. }
  547. else {
  548. $s->bindParam($key+1,$value,PDO::PARAM_STR);
  549. }
  550. }
  551. else {
  552. if (is_null($value)){
  553. $s->bindValue($key,null,PDO::PARAM_NULL);
  554. }
  555. elseif (!$this->flagUseStringOnlyBinding && RedBean_QueryWriter_AQueryWriter::canBeTreatedAsInt($value) && $value < 2147483648) {
  556. $s->bindParam($key,$value,PDO::PARAM_INT);
  557. }
  558. else {
  559. $s->bindParam($key,$value,PDO::PARAM_STR);
  560. }
  561. }
  562. }
  563. }
  564. /**
  565. * Runs a query. Internal function, available for subclasses. This method
  566. * runs the actual SQL query and binds a list of parameters to the query.
  567. * slots. The result of the query will be stored in the protected property
  568. * $rs (always array). The number of rows affected (result of rowcount, if supported by database)
  569. * is stored in protected property $affected_rows. If the debug flag is set
  570. * this function will send debugging output to screen buffer.
  571. *
  572. * @throws RedBean_Exception_SQL
  573. *
  574. * @param string $sql the SQL string to be send to database server
  575. * @param array $aValues the values that need to get bound to the query slots
  576. */
  577. protected function runQuery($sql,$aValues) {
  578. $this->connect();
  579. if ($this->debug && $this->logger) {
  580. $this->logger->log($sql, $aValues);
  581. }
  582. try {
  583. if (strpos('pgsql',$this->dsn)===0) {
  584. $s = $this->pdo->prepare($sql, array(PDO::PGSQL_ATTR_DISABLE_NATIVE_PREPARED_STATEMENT => true));
  585. }
  586. else {
  587. $s = $this->pdo->prepare($sql);
  588. }
  589. $this->bindParams( $s, $aValues );
  590. $s->execute();
  591. $this->affected_rows = $s->rowCount();
  592. if ($s->columnCount()) {
  593. $this->rs = $s->fetchAll();
  594. if ($this->debug && $this->logger) $this->logger->log('resultset: ' . count($this->rs) . ' rows');
  595. }
  596. else {
  597. $this->rs = array();
  598. }
  599. }catch(PDOException $e) {
  600. //Unfortunately the code field is supposed to be int by default (php)
  601. //So we need a property to convey the SQL State code.
  602. $x = new RedBean_Exception_SQL( $e->getMessage(), 0);
  603. $x->setSQLState( $e->getCode() );
  604. throw $x;
  605. }
  606. }
  607. /**
  608. * Runs a query and fetches results as a multi dimensional array.
  609. *
  610. * @param string $sql SQL to be executed
  611. *
  612. * @return array $results result
  613. */
  614. public function GetAll( $sql, $aValues=array() ) {
  615. $this->runQuery($sql,$aValues);
  616. return $this->rs;
  617. }
  618. /**
  619. * Runs a query and fetches results as a column.
  620. *
  621. * @param string $sql SQL Code to execute
  622. *
  623. * @return array $results Resultset
  624. */
  625. public function GetCol($sql, $aValues=array()) {
  626. $rows = $this->GetAll($sql,$aValues);
  627. $cols = array();
  628. if ($rows && is_array($rows) && count($rows)>0) {
  629. foreach ($rows as $row) {
  630. $cols[] = array_shift($row);
  631. }
  632. }
  633. return $cols;
  634. }
  635. /**
  636. * Runs a query an returns results as a single cell.
  637. *
  638. * @param string $sql SQL to execute
  639. *
  640. * @return mixed $cellvalue result cell
  641. */
  642. public function GetCell($sql, $aValues=array()) {
  643. $arr = $this->GetAll($sql,$aValues);
  644. $row1 = array_shift($arr);
  645. $col1 = array_shift($row1);
  646. return $col1;
  647. }
  648. /**
  649. * Runs a query and returns a flat array containing the values of
  650. * one row.
  651. *
  652. * @param string $sql SQL to execute
  653. *
  654. * @return array $row result row
  655. */
  656. public function GetRow($sql, $aValues=array()) {
  657. $arr = $this->GetAll($sql, $aValues);
  658. return array_shift($arr);
  659. }
  660. /**
  661. * Executes SQL code and allows key-value binding.
  662. * This function allows you to provide an array with values to bind
  663. * to query parameters. For instance you can bind values to question
  664. * marks in the query. Each value in the array corresponds to the
  665. * question mark in the query that matches the position of the value in the
  666. * array. You can also bind values using explicit keys, for instance
  667. * array(":key"=>123) will bind the integer 123 to the key :key in the
  668. * SQL. This method has no return value.
  669. *
  670. * @param string $sql SQL Code to execute
  671. * @param array $aValues Values to bind to SQL query
  672. *
  673. * @return void
  674. */
  675. public function Execute( $sql, $aValues=array() ) {
  676. $this->runQuery($sql,$aValues);
  677. return $this->affected_rows;
  678. }
  679. /**
  680. * Escapes a string for use in SQL using the currently selected
  681. * PDO driver.
  682. *
  683. * @param string $string string to be escaped
  684. *
  685. * @return string $string escaped string
  686. */
  687. public function Escape( $str ) {
  688. $this->connect();
  689. return substr(substr($this->pdo->quote($str), 1), 0, -1);
  690. }
  691. /**
  692. * Returns the latest insert ID if driver does support this
  693. * feature.
  694. *
  695. * @return integer $id primary key ID
  696. */
  697. public function GetInsertID() {
  698. $this->connect();
  699. return (int) $this->pdo->lastInsertId();
  700. }
  701. /**
  702. * Returns the number of rows affected by the most recent query
  703. * if the currently selected PDO driver supports this feature.
  704. *
  705. * @return integer $numOfRows number of rows affected
  706. */
  707. public function Affected_Rows() {
  708. $this->connect();
  709. return (int) $this->affected_rows;
  710. }
  711. /**
  712. * Toggles debug mode. In debug mode the driver will print all
  713. * SQL to the screen together with some information about the
  714. * results. All SQL code that passes through the driver will be
  715. * passes on to the screen for inspection.
  716. * This method has no return value.
  717. *
  718. * Additionally you can inject RedBean_ILogger implementation
  719. * where you can define your own log() method
  720. *
  721. * @param boolean $trueFalse turn on/off
  722. * @param RedBean_ILogger $logger
  723. *
  724. * @return void
  725. */
  726. public function setDebugMode( $tf, $logger = NULL ) {
  727. $this->connect();
  728. $this->debug = (bool)$tf;
  729. if ($this->debug and !$logger) $logger = new RedBean_Logger();
  730. $this->setLogger($logger);
  731. }
  732. /**
  733. * Injects RedBean_ILogger object.
  734. *
  735. * @param RedBean_ILogger $logger
  736. */
  737. public function setLogger( RedBean_ILogger $logger ) {
  738. $this->logger = $logger;
  739. }
  740. /**
  741. * Gets RedBean_ILogger object.
  742. *
  743. * @return RedBean_ILogger
  744. */
  745. public function getLogger() {
  746. return $this->logger;
  747. }
  748. /**
  749. * Starts a transaction.
  750. * This method is part of the transaction mechanism of
  751. * RedBeanPHP. All queries in a transaction are executed together.
  752. * In case of an error all commands will be rolled back so none of the
  753. * SQL in the transaction will affect the DB. Using transactions is
  754. * considered best practice.
  755. * This method has no return value.
  756. *
  757. * @return void
  758. */
  759. public function StartTrans() {
  760. $this->connect();
  761. $this->pdo->beginTransaction();
  762. }
  763. /**
  764. * Commits a transaction.
  765. * This method is part of the transaction mechanism of
  766. * RedBeanPHP. All queries in a transaction are executed together.
  767. * In case of an error all commands will be rolled back so none of the
  768. * SQL in the transaction will affect the DB. Using transactions is
  769. * considered best practice.
  770. * This method has no return value.
  771. *
  772. * @return void
  773. */
  774. public function CommitTrans() {
  775. $this->connect();
  776. $this->pdo->commit();
  777. }
  778. /**
  779. * Rolls back a transaction.
  780. * This method is part of the transaction mechanism of
  781. * RedBeanPHP. All queries in a transaction are executed together.
  782. * In case of an error all commands will be rolled back so none of the
  783. * SQL in the transaction will affect the DB. Using transactions is
  784. * considered best practice.
  785. * This method has no return value.
  786. *
  787. * @return void
  788. */
  789. public function FailTrans() {
  790. $this->connect();
  791. $this->pdo->rollback();
  792. }
  793. /**
  794. * Returns the name of the database type/brand: i.e. mysql, db2 etc.
  795. *
  796. * @return string $typeName database identification
  797. */
  798. public function getDatabaseType() {
  799. $this->connect();
  800. return $this->pdo->getAttribute(PDO::ATTR_DRIVER_NAME);
  801. }
  802. /**
  803. * Returns the version number of the database.
  804. *
  805. * @return mixed $version version number of the database
  806. */
  807. public function getDatabaseVersion() {
  808. $this->connect();
  809. return $this->pdo->getAttribute(PDO::ATTR_CLIENT_VERSION);
  810. }
  811. /**
  812. * Returns the underlying PHP PDO instance.
  813. *
  814. * @return PDO $pdo PDO instance used by PDO wrapper
  815. */
  816. public function getPDO() {
  817. $this->connect();
  818. return $this->pdo;
  819. }
  820. /**
  821. * Closes database connection by destructing PDO.
  822. */
  823. public function close() {
  824. $this->pdo = null;
  825. $this->isConnected = false;
  826. }
  827. /**
  828. * Returns TRUE if the current PDO instance is connected.
  829. *
  830. * @return boolean $yesNO
  831. */
  832. public function isConnected() {
  833. if (!$this->isConnected && !$this->pdo) return false;
  834. return true;
  835. }
  836. }
  837. /**
  838. * RedBean_OODBBean (Object Oriented DataBase Bean)
  839. *
  840. * @file RedBean/RedBean_OODBBean.php
  841. * @description The Bean class used for passing information
  842. *
  843. * @author Gabor de Mooij and the RedBeanPHP community
  844. * @license BSD/GPLv2
  845. *
  846. *
  847. * copyright (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community.
  848. * This source file is subject to the BSD/GPLv2 License that is bundled
  849. * with this source code in the file license.txt.
  850. */
  851. class RedBean_OODBBean implements IteratorAggregate, ArrayAccess, Countable {
  852. /**
  853. * Reference to NULL property for magic getter.
  854. * @var Null $null
  855. */
  856. private $null = null;
  857. /**
  858. * Properties of the bean. These are kept in a private
  859. * array called properties and exposed through the array interface.
  860. * @var array $properties
  861. */
  862. private $properties = array();
  863. /**
  864. * Meta Data storage. This is the internal property where all
  865. * Meta information gets stored.
  866. * @var array
  867. */
  868. private $__info = NULL;
  869. /**
  870. * Contains a BeanHelper to access service objects like
  871. * te association manager and OODB.
  872. * @var RedBean_BeanHelper
  873. */
  874. private $beanHelper = NULL;
  875. /**
  876. * Contains the latest Fetch Type.
  877. * A Fetch Type is a preferred type for the next nested bean.
  878. * @var null
  879. */
  880. private $fetchType = NULL;
  881. /** Returns the alias for a type
  882. *
  883. * @param $type aliased type
  884. *
  885. * @return string $type type
  886. */
  887. private function getAlias( $type ) {
  888. if ($this->fetchType) {
  889. $type = $this->fetchType;
  890. $this->fetchType = null;
  891. }
  892. return $type;
  893. }
  894. /**
  895. * Sets the Bean Helper. Normally the Bean Helper is set by OODB.
  896. * Here you can change the Bean Helper. The Bean Helper is an object
  897. * providing access to a toolbox for the bean necessary to retrieve
  898. * nested beans (bean lists: ownBean,sharedBean) without the need to
  899. * rely on static calls to the facade (or make this class dep. on OODB).
  900. *
  901. * @param RedBean_IBeanHelper $helper
  902. * @return void
  903. */
  904. public function setBeanHelper(RedBean_IBeanHelper $helper) {
  905. $this->beanHelper = $helper;
  906. }
  907. /**
  908. * Returns an ArrayIterator so you can treat the bean like
  909. * an array with the properties container as its contents.
  910. *
  911. * @return ArrayIterator $arrayIt an array iterator instance with $properties
  912. */
  913. public function getIterator() {
  914. return new ArrayIterator($this->properties);
  915. }
  916. /**
  917. * Imports all values in associative array $array. Every key is used
  918. * for a property and every value will be assigned to the property
  919. * identified by the key. So basically this method converts the
  920. * associative array to a bean by loading the array. You can filter
  921. * the values using the $selection parameter. If $selection is boolean
  922. * false, no filtering will be applied. If $selection is an array
  923. * only the properties specified (as values) in the $selection
  924. * array will be taken into account. To skip a property, omit it from
  925. * the $selection array. Also, instead of providing an array you may
  926. * pass a comma separated list of property names. This method is
  927. * chainable because it returns its own object.
  928. * Imports data into bean
  929. *
  930. * @param array $array what you want to import
  931. * @param string|array $selection selection of values
  932. * @param boolean $notrim if TRUE values will not be trimmed
  933. *
  934. * @return RedBean_OODBBean $this
  935. */
  936. public function import( $arr, $selection=false, $notrim=false ) {
  937. if (is_string($selection)) $selection = explode(',',$selection);
  938. //trim whitespaces
  939. if (!$notrim && is_array($selection)) foreach($selection as $k=>$s){ $selection[$k]=trim($s); }
  940. foreach($arr as $k=>$v) {
  941. if ($k!='__info') {
  942. if (!$selection || ($selection && in_array($k,$selection))) {
  943. $this->$k = $v;
  944. }
  945. }
  946. }
  947. return $this;
  948. }
  949. /**
  950. * Very superficial export function
  951. * @return array $properties
  952. */
  953. public function getProperties() {
  954. return $this->properties;
  955. }
  956. /**
  957. * Exports the bean as an array.
  958. * This function exports the contents of a bean to an array and returns
  959. * the resulting array. If $meta eq uals boolean TRUE, then the array will
  960. * also contain the __info section containing the meta data inside the
  961. * RedBean_OODBBean Bean object.
  962. * @param boolean $meta
  963. * @return array $arr
  964. */
  965. public function export($meta = false) {
  966. //$arr = $this->properties;
  967. $arr=array();
  968. foreach($this as $k=>$v) {
  969. if (is_array($v)) foreach($v as $i=>$b) $v[$i]=$b->export();
  970. $arr[$k] = $v;
  971. }
  972. if ($meta) $arr['__info'] = $this->__info;
  973. return $arr;
  974. }
  975. /**
  976. * Exports the bean to an object.
  977. * This function exports the contents of a bean to an object.
  978. * @param object $obj
  979. * @return array $arr
  980. */
  981. public function exportToObj($obj) {
  982. foreach($this->properties as $k=>$v) {
  983. if (!is_array($v) && !is_object($v))
  984. $obj->$k = $v;
  985. }
  986. }
  987. /**
  988. * Implements isset() function for use as an array.
  989. * Returns whether bean has an element with key
  990. * named $property. Returns TRUE if such an element exists
  991. * and FALSE otherwise.
  992. * @param string $property
  993. * @return boolean $hasProperty
  994. */
  995. public function __isset($property) {
  996. return (isset($this->properties[$property]));
  997. }
  998. /**
  999. * Returns the ID of the bean no matter what the ID field is.
  1000. *
  1001. * @return string $id record Identifier for bean
  1002. */
  1003. public function getID() {
  1004. return (string) $this->id;
  1005. }
  1006. /**
  1007. * Unsets a property. This method will load the property first using
  1008. * __get.
  1009. *
  1010. * @param string $property property
  1011. *
  1012. * @return void
  1013. */
  1014. public function __unset($property) {
  1015. $this->__get($property);
  1016. $fieldLink = $property.'_id';
  1017. if (isset($this->$fieldLink)) {
  1018. //wanna unset a bean reference?
  1019. $this->$fieldLink = null;
  1020. }
  1021. if ((isset($this->properties[$property]))) {
  1022. unset($this->properties[$property]);
  1023. }
  1024. }
  1025. /**
  1026. * Removes a property from the properties list without invoking
  1027. * an __unset on the bean.
  1028. *
  1029. * @param string $property property that needs to be unset
  1030. *
  1031. * @return void
  1032. */
  1033. public function removeProperty( $property ) {
  1034. unset($this->properties[$property]);
  1035. }
  1036. /**
  1037. * Magic Getter. Gets the value for a specific property in the bean.
  1038. * If the property does not exist this getter will make sure no error
  1039. * occurs. This is because RedBean allows you to query (probe) for
  1040. * properties. If the property can not be found this method will
  1041. * return NULL instead.
  1042. * @param string $property
  1043. * @return mixed $value
  1044. */
  1045. public function &__get( $property ) {
  1046. if ($this->beanHelper)
  1047. $toolbox = $this->beanHelper->getToolbox();
  1048. if (!isset($this->properties[$property])) {
  1049. $fieldLink = $property.'_id';
  1050. /**
  1051. * All this magic can be become very complex quicly. For instance,
  1052. * my PHP CLI produced a segfault while testing this code. Turns out that
  1053. * if fieldlink equals idfield, scripts tend to recusrively load beans and
  1054. * instead of giving a clue they simply crash and burn isnt that nice?
  1055. */
  1056. if (isset($this->$fieldLink) && $fieldLink != $this->getMeta('sys.idfield')) {
  1057. $this->setMeta('tainted',true);
  1058. $type = $this->getAlias($property);
  1059. $targetType = $this->properties[$fieldLink];
  1060. $bean = $toolbox->getRedBean()->load($type,$targetType);
  1061. //return $bean;
  1062. $this->properties[$property] = $bean;
  1063. return $this->properties[$property];
  1064. }
  1065. if (strpos($property,'own')===0) {
  1066. $firstCharCode = ord(substr($property,3,1));
  1067. if ($firstCharCode>=65 && $firstCharCode<=90) {
  1068. $type = (__lcfirst(str_replace('own','',$property)));
  1069. $myFieldLink = $this->getMeta('type').'_id';
  1070. $beans = $toolbox->getRedBean()->find($type,array(),array(" $myFieldLink = ? ",array($this->getID())));
  1071. $this->properties[$property] = $beans;
  1072. $this->setMeta('sys.shadow.'.$property,$beans);
  1073. $this->setMeta('tainted',true);
  1074. return $this->properties[$property];
  1075. }
  1076. }
  1077. if (strpos($property,'shared')===0) {
  1078. $firstCharCode = ord(substr($property,6,1));
  1079. if ($firstCharCode>=65 && $firstCharCode<=90) {
  1080. $type = (__lcfirst(str_replace('shared','',$property)));
  1081. $keys = $toolbox->getRedBean()->getAssociationManager()->related($this,$type);
  1082. if (!count($keys)) $beans = array(); else
  1083. $beans = $toolbox->getRedBean()->batch($type,$keys);
  1084. $this->properties[$property] = $beans;
  1085. $this->setMeta('sys.shadow.'.$property,$beans);
  1086. $this->setMeta('tainted',true);
  1087. return $this->properties[$property];
  1088. }
  1089. }
  1090. return $this->null;
  1091. }
  1092. return $this->properties[$property];
  1093. }
  1094. /**
  1095. * Magic Setter. Sets the value for a specific property.
  1096. * This setter acts as a hook for OODB to mark beans as tainted.
  1097. * The tainted meta property can be retrieved using getMeta("tainted").
  1098. * The tainted meta property indicates whether a bean has been modified and
  1099. * can be used in various caching mechanisms.
  1100. * @param string $property
  1101. * @param mixed $value
  1102. */
  1103. public function __set($property,$value) {
  1104. $this->__get($property);
  1105. $this->setMeta('tainted',true);
  1106. $linkField = $property.'_id';
  1107. if (isset($this->properties[$linkField]) && !($value instanceof RedBean_OODBBean)) {
  1108. if (is_null($value) || $value === false) {
  1109. return $this->__unset($property);
  1110. }
  1111. else {
  1112. throw new RedBean_Exception_Security('Cannot cast to bean.');
  1113. }
  1114. }
  1115. if ($value===false) {
  1116. $value = '0';
  1117. }
  1118. if ($value===true) {
  1119. $value = '1';
  1120. }
  1121. $this->properties[$property] = $value;
  1122. }
  1123. /**
  1124. * Returns the value of a meta property. A meta property
  1125. * contains extra information about the bean object that will not
  1126. * get stored in the database. Meta information is used to instruct
  1127. * RedBean as well as other systems how to deal with the bean.
  1128. * For instance: $bean->setMeta("buildcommand.unique.0", array(
  1129. * "column1", "column2", "column3") );
  1130. * Will add a UNIQUE constaint for the bean on columns: column1, column2 and
  1131. * column 3.
  1132. * To access a Meta property we use a dot separated notation.
  1133. * If the property cannot be found this getter will return NULL instead.
  1134. * @param string $path
  1135. * @param mixed $default
  1136. * @return mixed $value
  1137. */
  1138. public function getMeta($path,$default = NULL) {
  1139. return (isset($this->__info[$path])) ? $this->__info[$path] : $default;
  1140. }
  1141. /**
  1142. * Stores a value in the specified Meta information property. $value contains
  1143. * the value you want to store in the Meta section of the bean and $path
  1144. * specifies the dot separated path to the property. For instance "my.meta.property".
  1145. * If "my" and "meta" do not exist they will be created automatically.
  1146. * @param string $path
  1147. * @param mixed $value
  1148. */
  1149. public function setMeta($path,$value) {
  1150. $this->__info[$path] = $value;
  1151. }
  1152. /**
  1153. * Copies the meta information of the specified bean
  1154. * This is a convenience method to enable you to
  1155. * exchange meta information easily.
  1156. * @param RedBean_OODBBean $bean
  1157. * @return RedBean_OODBBean
  1158. */
  1159. public function copyMetaFrom(RedBean_OODBBean $bean) {
  1160. $this->__info = $bean->__info;
  1161. return $this;
  1162. }
  1163. /**
  1164. * Reroutes a call to Model if exists. (new fuse)
  1165. * @param string $method
  1166. * @param array $args
  1167. * @return mixed $mixed
  1168. */
  1169. public function __call($method, $args) {
  1170. if (!isset($this->__info['model'])) {
  1171. $modelName = RedBean_ModelHelper::getModelName( $this->getMeta('type'), $this );
  1172. if (!class_exists($modelName)) return null;
  1173. $obj = RedBean_ModelHelper::factory($modelName);
  1174. $obj->loadBean($this);
  1175. $this->__info['model'] = $obj;
  1176. }
  1177. if (!method_exists($this->__info['model'],$method)) return null;
  1178. return call_user_func_array(array($this->__info['model'],$method), $args);
  1179. }
  1180. /**
  1181. * Implementation of __toString Method
  1182. * Routes call to Model.
  1183. * @return string $string
  1184. */
  1185. public function __toString() {
  1186. $string = $this->__call('__toString',array());
  1187. if ($string === null) {
  1188. return json_encode($this->properties);
  1189. }
  1190. else {
  1191. return $string;
  1192. }
  1193. }
  1194. /**
  1195. * Implementation of Array Access Interface, you can access bean objects
  1196. * like an array.
  1197. * Call gets routed to __set.
  1198. *
  1199. * @param mixed $offset offset string
  1200. * @param mixed $value value
  1201. *
  1202. * @return void
  1203. */
  1204. public function offsetSet($offset, $value) {
  1205. $this->__set($offset, $value);
  1206. }
  1207. /**
  1208. * Implementation of Array Access Interface, you can access bean objects
  1209. * like an array.
  1210. *
  1211. * @param mixed $offset property
  1212. *
  1213. * @return
  1214. */
  1215. public function offsetExists($offset) {
  1216. return isset($this->properties[$offset]);
  1217. }
  1218. /**
  1219. * Implementation of Array Access Interface, you can access bean objects
  1220. * like an array.
  1221. * Unsets a value from the array/bean.
  1222. *
  1223. * @param mixed $offset property
  1224. *
  1225. * @return
  1226. */
  1227. public function offsetUnset($offset) {
  1228. unset($this->properties[$offset]);
  1229. }
  1230. /**
  1231. * Implementation of Array Access Interface, you can access bean objects
  1232. * like an array.
  1233. * Returns value of a property.
  1234. *
  1235. * @param mixed $offset property
  1236. *
  1237. * @return
  1238. */
  1239. public function offsetGet($offset) {
  1240. return $this->__get($offset);
  1241. }
  1242. /**
  1243. * Chainable method to cast a certain ID to a bean; for instance:
  1244. * $person = $club->fetchAs('person')->member;
  1245. * This will load a bean of type person using member_id as ID.
  1246. *
  1247. * @param string $type preferred fetch type
  1248. *
  1249. * @return RedBean_OODBBean
  1250. */
  1251. public function fetchAs($type) {
  1252. $this->fetchType = $type;
  1253. return $this;
  1254. }
  1255. /**
  1256. * Implementation of Countable interface. Makes it possible to use
  1257. * count() function on a bean.
  1258. *
  1259. * @return integer $numberOfProperties number of properties in the bean.
  1260. */
  1261. public function count() {
  1262. return count($this->properties);
  1263. }
  1264. /**
  1265. * Checks wether a bean is empty or not.
  1266. * A bean is empty if it has no other properties than the id field OR
  1267. * if all the other property are empty().
  1268. *
  1269. * @return boolean
  1270. */
  1271. public function isEmpty() {
  1272. $empty = true;
  1273. foreach($this->properties as $key=>$value) {
  1274. if ($key=='id') continue;
  1275. if (!empty($value)) {
  1276. $empty = false;
  1277. }
  1278. }
  1279. return $empty;
  1280. }
  1281. }
  1282. /**
  1283. * Observable
  1284. * Base class for Observables
  1285. *
  1286. * @file RedBean/Observable.php
  1287. * @description Part of the observer pattern in RedBean
  1288. *
  1289. * @author Gabor de Mooij and the RedBeanPHP community
  1290. * @license BSD/GPLv2
  1291. *
  1292. *
  1293. * copyright (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community.
  1294. * This source file is subject to the BSD/GPLv2 License that is bundled
  1295. * with this source code in the file license.txt.
  1296. */
  1297. abstract class RedBean_Observable {
  1298. /**
  1299. * Array that keeps track of observers.
  1300. * @var array
  1301. */
  1302. private $observers = array();
  1303. /**
  1304. * Implementation of the Observer Pattern.
  1305. * Adds a listener to this instance.
  1306. * This method can be used to attach an observer to an object.
  1307. * You can subscribe to a specific event by providing the ID
  1308. * of the event you are interested in. Once the event occurs
  1309. * the observable will notify the listeners by calling
  1310. * onEvent(); providing the event ID and either a bean or
  1311. * an information array.
  1312. *
  1313. * @param string $eventname event
  1314. * @param RedBean_Observer $observer observer
  1315. *
  1316. * @return void
  1317. */
  1318. public function addEventListener( $eventname, RedBean_Observer $observer ) {
  1319. if (!isset($this->observers[ $eventname ])) {
  1320. $this->observers[ $eventname ] = array();
  1321. }
  1322. foreach($this->observers[$eventname] as $o) if ($o==$observer) return;
  1323. $this->observers[ $eventname ][] = $observer;
  1324. }
  1325. /**
  1326. * Implementation of the Observer Pattern.
  1327. * Sends an event (signal) to the registered listeners
  1328. * This method is provided by the abstract class Observable for
  1329. * convience. Observables can use this method to notify their
  1330. * observers by sending an event ID and information parameter.
  1331. *
  1332. * @param string $eventname eventname
  1333. * @param mixed $info info
  1334. * @return unknown_ty
  1335. */
  1336. public function signal( $eventname, $info ) {
  1337. if (!isset($this->observers[ $eventname ])) {
  1338. $this->observers[ $eventname ] = array();
  1339. }
  1340. foreach($this->observers[$eventname] as $observer) {
  1341. $observer->onEvent( $eventname, $info );
  1342. }
  1343. }
  1344. }
  1345. /**
  1346. * Observer
  1347. *
  1348. * @file RedBean/Observer.php
  1349. * @description Part of the observer pattern in RedBean
  1350. *
  1351. * @author Gabor de Mooijand the RedBeanPHP community
  1352. * @license BSD/GPLv2
  1353. *
  1354. * copyright (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community.
  1355. * This source file is subject to the BSD/GPLv2 License that is bundled
  1356. * with this source code in the file license.txt.
  1357. */
  1358. interface RedBean_Observer {
  1359. /**
  1360. * Part of the RedBean Observer Infrastructure.
  1361. * The on-event method is called by an observable once the
  1362. * event the observer has been registered for occurs.
  1363. * Once the even occurs, the observable will signal the observer
  1364. * using this method, sending the event name and the bean or
  1365. * an information array.
  1366. *
  1367. * @param string $eventname
  1368. * @param RedBean_OODBBean mixed $info
  1369. */
  1370. public function onEvent( $eventname, $bean );
  1371. }
  1372. /**
  1373. * Adapter Interface
  1374. *
  1375. * @file RedBean/Adapter.php
  1376. * @description Describes the API for a RedBean Database Adapter.
  1377. * @author Gabor de Mooij and the RedBeanPHP Community
  1378. * @license BSD/GPLv2
  1379. *
  1380. * (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community.
  1381. * This source file is subject to the BSD/GPLv2 License that is bundled
  1382. * with this source code in the file license.txt.
  1383. *
  1384. */
  1385. interface RedBean_Adapter {
  1386. /**
  1387. * Returns the latest SQL statement
  1388. *
  1389. * @return string $SQLString SQLString
  1390. */
  1391. public function getSQL();
  1392. /**
  1393. * Escapes a value for usage in an SQL statement
  1394. *
  1395. * @param string $sqlvalue value
  1396. */
  1397. public function escape( $sqlvalue );
  1398. /**
  1399. * Executes an SQL Statement using an array of values to bind
  1400. * If $noevent is TRUE then this function will not signal its
  1401. * observers to notify about the SQL execution; this to prevent
  1402. * infinite recursion when using observers.
  1403. *
  1404. * @param string $sql SQL
  1405. * @param array $aValues values
  1406. * @param boolean $noevent no event firing
  1407. */
  1408. public function exec( $sql , $aValues=array(), $noevent=false);
  1409. /**
  1410. * Executes an SQL Query and returns a resultset.
  1411. * This method returns a multi dimensional resultset similar to getAll
  1412. * The values array can be used to bind values to the place holders in the
  1413. * SQL query.
  1414. *
  1415. * @param string $sql SQL
  1416. * @param array $aValues values
  1417. */
  1418. public function get( $sql, $aValues = array() );
  1419. /**
  1420. * Executes an SQL Query and returns a resultset.
  1421. * This method returns a single row (one array) resultset.
  1422. * The values array can be used to bind values to the place holders in the
  1423. * SQL query.
  1424. *
  1425. * @param string $sql SQL
  1426. * @param array $aValues values to bind
  1427. *
  1428. * @return array $aMultiDimArray row
  1429. */
  1430. public function getRow( $sql, $aValues = array() );
  1431. /**
  1432. * Executes an SQL Query and returns a resultset.
  1433. * This method returns a single column (one array) resultset.
  1434. * The values array can be used to bind values to the place holders in the
  1435. * SQL query.
  1436. *
  1437. * @param string $sql SQL
  1438. * @param array $aValues values to bind
  1439. *
  1440. * @return array $aSingleDimArray column
  1441. */
  1442. public function getCol( $sql, $aValues = array() );
  1443. /**
  1444. * Executes an SQL Query and returns a resultset.
  1445. * This method returns a single cell, a scalar value as the resultset.
  1446. * The values array can be used to bind values to the place holders in the
  1447. * SQL query.
  1448. *
  1449. * @param string $sql SQL
  1450. * @param array $aValues values to bind
  1451. *
  1452. * @return string $sSingleValue value from cell
  1453. */
  1454. public function getCell( $sql, $aValues = array() );
  1455. /**
  1456. * Executes the SQL query specified in $sql and takes
  1457. * the first two columns of the resultset. This function transforms the
  1458. * resultset into an associative array. Values from the the first column will
  1459. * serve as keys while the values of the second column will be used as values.
  1460. * The values array can be used to bind values to the place holders in the
  1461. * SQL query.
  1462. *
  1463. * @param string $sql SQL
  1464. * @param array $values values to bind
  1465. *
  1466. * @return array $associativeArray associative array result set
  1467. */
  1468. public function getAssoc( $sql, $values = array() );
  1469. /**
  1470. * Returns the latest insert ID.
  1471. *
  1472. * @return integer $id primary key ID
  1473. */
  1474. public function getInsertID();
  1475. /**
  1476. * Returns the number of rows that have been
  1477. * affected by the last update statement.
  1478. *
  1479. * @return integer $count number of rows affected
  1480. */
  1481. public function getAffectedRows();
  1482. /**
  1483. * Returns the original database resource. This is useful if you want to
  1484. * perform operations on the driver directly instead of working with the
  1485. * adapter. RedBean will only access the adapter and never to talk
  1486. * directly to the driver though.
  1487. *
  1488. * @return object $driver driver
  1489. */
  1490. public function getDatabase();
  1491. /**
  1492. * This method is part of the RedBean Transaction Management
  1493. * mechanisms.
  1494. * Starts a transaction.
  1495. */
  1496. public function startTransaction();
  1497. /**
  1498. * This method is part of the RedBean Transaction Management
  1499. * mechanisms.
  1500. * Commits the transaction.
  1501. */
  1502. public function commit();
  1503. /**
  1504. * This method is part of the RedBean Transaction Management
  1505. * mechanisms.
  1506. * Rolls back the transaction.
  1507. */
  1508. public function rollback();
  1509. /**
  1510. * Closes database connection.
  1511. */
  1512. public function close();
  1513. }
  1514. /**
  1515. * DBAdapter (Database Adapter)
  1516. * @file RedBean/Adapter/DBAdapter.php
  1517. * @description An adapter class to connect various database systems to RedBean
  1518. * @author Gabor de Mooij and the RedBeanPHP Community.
  1519. * @license BSD/GPLv2
  1520. *
  1521. *
  1522. * (c) copyright G.J.G.T. (Gabor) de Mooij and the RedBeanPHP community.
  1523. * This source file is subject to the BSD/GPLv2 License that is bundled
  1524. * with this source code in the file license.txt.
  1525. */
  1526. class RedBean_Adapter_DBAdapter extends RedBean_Observable implements RedBean_Adapter {
  1527. /**
  1528. * ADODB compatible class
  1529. * @var RedBean_Driver
  1530. */
  1531. private $db = null;
  1532. /**
  1533. * Contains SQL snippet
  1534. * @var string
  1535. */
  1536. private $sql = '';
  1537. /**
  1538. * Constructor.
  1539. * Creates an instance of the RedBean Adapter Class.
  1540. * This class provides an interface for RedBean to work
  1541. * with ADO compatible DB instances.
  1542. *
  1543. * @param RedBean_Driver $database ADO Compatible DB Instance
  1544. */
  1545. public function __construct($database) {
  1546. $this->db = $database;
  1547. }
  1548. /**
  1549. * Returns the latest SQL Statement.
  1550. *
  1551. * @return string $SQL latest SQL statement
  1552. */
  1553. public function getSQL() {
  1554. return $this->sql;
  1555. }
  1556. /**
  1557. * Escapes a string for use in a Query.
  1558. *
  1559. * @param string $sqlvalue SQL value to escape
  1560. *
  1561. * @return string $escapedValue escaped value
  1562. */
  1563. public function escape( $sqlvalue ) {
  1564. return $this->db->Escape($sqlvalue);
  1565. }
  1566. /**
  1567. * Executes SQL code; any query without
  1568. * returning a resultset.
  1569. * This function allows you to provide an array with values to bind
  1570. * to query parameters. For instance you can bind values to question
  1571. * marks in the query. Each value in the array corresponds to the
  1572. * question mark in the query that matches the position of the value in the
  1573. * array. You can also bind values using explicit keys, for instance
  1574. * array(":key"=>123) will bind the integer 123 to the key :key in the
  1575. * SQL.
  1576. *
  1577. * @param string $sql SQL Code to execute
  1578. * @param array $values assoc. array binding values
  1579. * @param boolean $noevent if TRUE this will suppress the event 'sql_exec'
  1580. *
  1581. * @return mixed $undefSet whatever driver returns, undefined
  1582. */
  1583. public function exec( $sql , $aValues=array(), $noevent=false) {
  1584. if (!$noevent) {
  1585. $this->sql = $sql;
  1586. $this->signal('sql_exec', $this);
  1587. }
  1588. return $this->db->Execute( $sql, $aValues );
  1589. }
  1590. /**
  1591. * Multi array SQL fetch. Fetches a multi dimensional array.
  1592. * This function allows you to provide an array with values to bind
  1593. * to query parameters. For instance you can bind values to question
  1594. * marks in the query. Each value in the array corresponds to the
  1595. * question mark in the query that matches the position of the value in the
  1596. * array. You can also bind values using explicit keys, for instance
  1597. * array(":key"=>123) will bind the integer 123 to the key :key in the
  1598. * SQL.
  1599. *
  1600. * @param string $sql SQL code to execute
  1601. * @param array $values assoc. array binding values
  1602. *
  1603. * @return array $result two dimensional array result set
  1604. */
  1605. public function get( $sql, $aValues = array() ) {
  1606. $this->sql = $sql;
  1607. $this->signal('sql_exec', $this);
  1608. return $this->db->GetAll( $sql,$aValues );
  1609. }
  1610. /**
  1611. * Executes SQL and fetches a single row.
  1612. * This function allows you to provide an array with values to bind
  1613. * to query parameters. For instance you can bind values to question
  1614. * marks in the query. Each value in the array corresponds to the
  1615. * question mark in the query that matches the position of the value in the
  1616. * array. You can also bind values using explicit keys, for instance
  1617. * array(":key"=>123) will bind the integer 123 to the key :key in the
  1618. * SQL.
  1619. *
  1620. * @param string $sql SQL code to execute
  1621. * @param array $values assoc. array binding values
  1622. *
  1623. * @return array $result one dimensional array result set
  1624. */
  1625. public function getRow( $sql, $aValues = array() ) {
  1626. $this->sql = $sql;
  1627. $this->signal('sql_exec', $this);
  1628. return $this->db->GetRow( $sql,$aValues );
  1629. }
  1630. /**
  1631. * Executes SQL and returns a one dimensional array result set.
  1632. * This function rotates the result matrix to obtain a column result set.
  1633. * This function allows you to provide an array with values to bind
  1634. * to query parameters. For instance you can bind values to question
  1635. * marks in the query. Each value in the array corresponds to the
  1636. * question mark in the query that matches the position of the value in the
  1637. * array. You can also bind values using explicit keys, for instance
  1638. * array(":key"=>123) will bind the integer 123 to the key :key in the
  1639. * SQL.
  1640. *
  1641. * @param string $sql SQL code to execute
  1642. * @param array $values assoc. array binding values
  1643. *
  1644. * @return array $result one dimensional array result set
  1645. */
  1646. public function getCol( $sql, $aValues = array() ) {
  1647. $this->sql = $sql;
  1648. $this->signal('sql_exec', $this);
  1649. return $this->db->GetCol( $sql,$aValues );
  1650. }
  1651. /**
  1652. * Executes an SQL Query and fetches the first two columns only.
  1653. * Then this function builds an associative array using the first
  1654. * column for the keys and the second result column for the
  1655. * values. For instance: SELECT id, name FROM... will produce
  1656. * an array like: id => name.
  1657. * This function allows you to provide an array with values to bind
  1658. * to query parameters. For instance you can bind values to question
  1659. * marks in the query. Each value in the array corresponds to the
  1660. * question mark in the query that matches the position of the value in the
  1661. * array. You can also bind values using explicit keys, for instance
  1662. * array(":key"=>123) will bind the integer 123 to the key :key in the
  1663. * SQL.
  1664. *
  1665. * @param string $sql SQL code to execute
  1666. * @param array $values assoc. array binding values
  1667. *
  1668. * @return array $result multi dimensional assoc. array result set
  1669. */
  1670. public function getAssoc( $sql, $aValues = array() ) {
  1671. $this->sql = $sql;
  1672. $this->signal('sql_exec', $this);
  1673. $rows = $this->db->GetAll( $sql, $aValues );
  1674. $assoc = array();
  1675. if ($rows) {
  1676. foreach($rows as $row) {
  1677. if (count($row)>0) {
  1678. if (count($row)>1) {
  1679. $key = array_shift($row);
  1680. $value = array_shift($row);
  1681. }
  1682. elseif (count($row)==1) {
  1683. $key = array_shift($row);
  1684. $value=$key;
  1685. }
  1686. $assoc[ $key ] = $value;
  1687. }
  1688. }
  1689. }
  1690. return $assoc;
  1691. }
  1692. /**
  1693. * Retrieves a single cell.
  1694. * This function allows you to provide an array with values to bind
  1695. * to query parameters. For instance you can bind values to question
  1696. * marks in the query. Each value in the array corresponds to the
  1697. * question mark in the query that matches the position of the value in the
  1698. * array. You can also bind values using explicit keys, for instance
  1699. * array(":key"=>123) will bind the integer 123 to the key :key in the
  1700. * SQL.
  1701. *
  1702. * @param string $sql sql code to execute
  1703. * @param array $values assoc. array binding values
  1704. *
  1705. * @return array $result scalar result set
  1706. */
  1707. public function getCell( $sql, $aValues = array(), $noSignal = null ) {
  1708. $this->sql = $sql;
  1709. if (!$noSignal) $this->signal('sql_exec', $this);
  1710. $arr = $this->db->getCol( $sql, $aValues );
  1711. if ($arr && is_array($arr)) return ($arr[0]); else return false;
  1712. }
  1713. /**
  1714. * Returns latest insert id, most recently inserted id.
  1715. *
  1716. * @return integer $id latest insert ID
  1717. */
  1718. public function getInsertID() {
  1719. return $this->db->getInsertID();
  1720. }
  1721. /**
  1722. * Returns number of affected rows.
  1723. *
  1724. * @return integer $numOfAffectRows
  1725. */
  1726. public function getAffectedRows() {
  1727. return $this->db->Affected_Rows();
  1728. }
  1729. /**
  1730. * Unwrap the original database object.
  1731. *
  1732. * @return RedBean_Driver $database returns the inner database object
  1733. */
  1734. public function getDatabase() {
  1735. return $this->db;
  1736. }
  1737. /**
  1738. * Transactions.
  1739. * Part of the transaction management infrastructure of RedBean.
  1740. * Starts a transaction.
  1741. */
  1742. public function startTransaction() {
  1743. return $this->db->StartTrans();
  1744. }
  1745. /**
  1746. * Transactions.
  1747. * Part of the transaction management infrastructure of RedBean.
  1748. * Commits a transaction.
  1749. */
  1750. public function commit() {
  1751. return $this->db->CommitTrans();
  1752. }
  1753. /**
  1754. * Transactions.
  1755. * Part of the transaction management infrastructure of RedBean.
  1756. * Rolls back transaction.
  1757. */
  1758. public function rollback() {
  1759. return $this->db->FailTrans();
  1760. }
  1761. /**
  1762. * Closes the database connection.
  1763. */
  1764. public function close() {
  1765. $this->db->close();
  1766. }
  1767. }
  1768. /**
  1769. * QueryWriter
  1770. * Interface for QueryWriters
  1771. *
  1772. * @file RedBean/QueryWriter.php
  1773. * @description Describes the API for a QueryWriter
  1774. * @author Gabor de Mooij and the RedBeanPHP community
  1775. * @license BSD/GPLv2
  1776. *
  1777. * Notes:
  1778. * - Whenever you see a parameter called $table or $type you should always
  1779. * be aware of the fact that this argument contains a Bean Type string, not the
  1780. * actual table name. These raw type names are passed to safeTable() to obtain the
  1781. * actual name of the database table. Don't let the names confuse you $type/$table
  1782. * refers to Bean Type, not physical database table names!
  1783. * - This is the interface for FLUID database drivers. Drivers intended to support
  1784. * just FROZEN mode should implement the IceWriter instead.
  1785. *
  1786. *
  1787. * copyright (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community.
  1788. * This source file is subject to the BSD/GPLv2 License that is bundled
  1789. * with this source code in the file license.txt.
  1790. */
  1791. interface RedBean_QueryWriter {
  1792. /**
  1793. * QueryWriter Constant Identifier.
  1794. * Identifies a situation in which a table has not been found in
  1795. * the database.
  1796. */
  1797. const C_SQLSTATE_NO_SUCH_TABLE = 1;
  1798. /**
  1799. * QueryWriter Constant Identifier.
  1800. * Identifies a situation in which a perticular column has not
  1801. * been found in the database.
  1802. */
  1803. const C_SQLSTATE_NO_SUCH_COLUMN = 2;
  1804. /**
  1805. * QueryWriter Constant Identifier.
  1806. * Identifies a situation in which a perticular column has not
  1807. * been found in the database.
  1808. */
  1809. const C_SQLSTATE_INTEGRITY_CONSTRAINT_VIOLATION = 3;
  1810. /**
  1811. * Returns the tables that are in the database.
  1812. *
  1813. * @return array $arrayOfTables list of tables
  1814. */
  1815. public function getTables();
  1816. /**
  1817. * This method should create a table for the bean.
  1818. * This methods accepts a type and infers the corresponding table name.
  1819. *
  1820. * @param string $type type of bean you want to create a table for
  1821. *
  1822. * @return void
  1823. */
  1824. public function createTable($type);
  1825. /**
  1826. * Returns an array containing all the columns of the specified type.
  1827. * The format of the return array looks like this:
  1828. * $field => $type where $field is the name of the column and $type
  1829. * is a database specific description of the datatype.
  1830. *
  1831. * This methods accepts a type and infers the corresponding table name.
  1832. *
  1833. * @param string $type type of bean you want to obtain a column list of
  1834. *
  1835. * @return array $listOfColumns list of columns ($field=>$type)
  1836. */
  1837. public function getColumns($type);
  1838. /**
  1839. * Returns the Column Type Code (integer) that corresponds
  1840. * to the given value type. This method is used to determine the minimum
  1841. * column type required to represent the given value.
  1842. *
  1843. * @param string $value value
  1844. *
  1845. * @return integer $type type
  1846. */
  1847. public function scanType($value, $alsoScanSpecialForTypes=false);
  1848. /**
  1849. * This method should add a column to a table.
  1850. * This methods accepts a type and infers the corresponding table name.
  1851. *
  1852. * @param string $type name of the table
  1853. * @param string $column name of the column
  1854. * @param integer $field data type for field
  1855. *
  1856. * @return void
  1857. *
  1858. */
  1859. public function addColumn($type, $column, $field);
  1860. /**
  1861. * This method should return a data type constant based on the
  1862. * SQL type definition. This function is meant to compare column data
  1863. * type to check whether a column is wide enough to represent the desired
  1864. * values.
  1865. *
  1866. * @param integer $typedescription SQL type description from database
  1867. *
  1868. * @return integer $type
  1869. */
  1870. public function code($typedescription);
  1871. /**
  1872. * This method should widen the column to the specified data type.
  1873. * This methods accepts a type and infers the corresponding table name.
  1874. *
  1875. * @param string $type type / table that needs to be adjusted
  1876. * @param string $column column that needs to be altered
  1877. * @param integer $datatype target data type
  1878. *
  1879. * @return void
  1880. */
  1881. public function widenColumn($type, $column, $datatype);
  1882. /**
  1883. * This method should update (or insert a record), it takes
  1884. * a table name, a list of update values ( $field => $value ) and an
  1885. * primary key ID (optional). If no primary key ID is provided, an
  1886. * INSERT will take place.
  1887. * Returns the new ID.
  1888. * This methods accepts a type and infers the corresponding table name.
  1889. *
  1890. * @param string $type name of the table to update
  1891. * @param array $updatevalues list of update values
  1892. * @param integer $id optional primary key ID value
  1893. *
  1894. * @return integer $id the primary key ID value of the new record
  1895. */
  1896. public function updateRecord($type, $updatevalues, $id=null);
  1897. /**
  1898. * This method should select a record. You should be able to provide a
  1899. * collection of conditions using the following format:
  1900. * array( $field1 => array($possibleValue1, $possibleValue2,... $possibleValueN ),
  1901. * ...$fieldN=>array(...));
  1902. * Also, additional SQL can be provided. This SQL snippet will be appended to the
  1903. * query string. If the $delete parameter is set to TRUE instead of selecting the
  1904. * records they will be deleted.
  1905. * This methods accepts a type and infers the corresponding table name.
  1906. *
  1907. * @param string $type type of bean to select records from
  1908. * @param array $cond conditions using the specified format
  1909. * @param string $asql additional sql
  1910. * @param boolean $delete IF TRUE delete records (optional)
  1911. * @param boolean $inverse IF TRUE inverse the selection (optional)
  1912. *
  1913. * @return array $records selected records
  1914. */
  1915. public function selectRecord($type, $conditions, $addSql = null, $delete = false, $inverse = false);
  1916. /**
  1917. * This method should add a UNIQUE constraint index to a table on columns $columns.
  1918. * This methods accepts a type and infers the corresponding table name.
  1919. *
  1920. * @param string $type type
  1921. * @param array $columnsPartOfIndex columns to include in index
  1922. *
  1923. * @return void
  1924. */
  1925. public function addUniqueIndex($type,$columns);
  1926. /**
  1927. * This method should check whether the SQL state is in the list of specified states
  1928. * and returns true if it does appear in this list or false if it
  1929. * does not. The purpose of this method is to translate the database specific state to
  1930. * a one of the constants defined in this class and then check whether it is in the list
  1931. * of standard states provided.
  1932. *
  1933. * @param string $state sql state
  1934. * @param array $list list
  1935. *
  1936. * @return boolean $isInList
  1937. */
  1938. public function sqlStateIn( $state, $list );
  1939. /**
  1940. * This method should remove all beans of a certain type.
  1941. * This methods accepts a type and infers the corresponding table name.
  1942. *
  1943. * @param string $type bean type
  1944. *
  1945. * @return void
  1946. */
  1947. public function wipe($type);
  1948. /**
  1949. * This method should count the number of beans of the given type.
  1950. * This methods accepts a type and infers the corresponding table name.
  1951. *
  1952. * @param string $type type of bean to count
  1953. *
  1954. * @return integer $numOfBeans number of beans found
  1955. */
  1956. public function count($type);
  1957. /**
  1958. * This method should filter a column name so that it can
  1959. * be used safely in a query for a specific database.
  1960. *
  1961. * @param string $name the column name
  1962. * @param bool $noQuotes whether you want to omit quotes
  1963. *
  1964. * @return string $clean the clean version of the column name
  1965. */
  1966. public function safeColumn($name, $noQuotes = false);
  1967. /**
  1968. * This method should filter a type name so that it can
  1969. * be used safely in a query for a specific database. It actually
  1970. * converts a type to a table. TYPE -> TABLE
  1971. *
  1972. * @param string $name the name of the type
  1973. * @param bool $noQuotes whether you want to omit quotes in table name
  1974. *
  1975. * @return string $tablename clean table name for use in query
  1976. */
  1977. public function safeTable($name, $noQuotes = false);
  1978. /**
  1979. * This method should add a constraint. If one of the beans gets trashed
  1980. * the other, related bean should be removed as well.
  1981. *
  1982. * @param RedBean_OODBBean $bean1 first bean
  1983. * @param RedBean_OODBBean $bean2 second bean
  1984. *
  1985. * @return void
  1986. */
  1987. public function addConstraint( RedBean_OODBBean $bean1, RedBean_OODBBean $bean2 );
  1988. /**
  1989. * This method should add a foreign key from type and field to
  1990. * target type and target field.
  1991. * The foreign key is created without an action. On delete/update
  1992. * no action will be triggered. The FK is only used to allow database
  1993. * tools to generate pretty diagrams and to make it easy to add actions
  1994. * later on.
  1995. * This methods accepts a type and infers the corresponding table name.
  1996. *
  1997. *
  1998. * @param string $type type that will have a foreign key field
  1999. * @param string $targetType points to this type
  2000. * @param string $field field that contains the foreign key value
  2001. * @param string $targetField field where the fk points to
  2002. *
  2003. * @return void
  2004. */
  2005. public function addFK( $type, $targetType, $field, $targetField);
  2006. /**
  2007. * This method should add an index to a type and field with name
  2008. * $name.
  2009. * This methods accepts a type and infers the corresponding table name.
  2010. *
  2011. * @param $type type to add index to
  2012. * @param $name name of the new index
  2013. * @param $column field to index
  2014. *
  2015. * @return void
  2016. */
  2017. public function addIndex($type, $name, $column);
  2018. /**
  2019. * Returns a modified value from ScanType.
  2020. * Used for special types.
  2021. *
  2022. * @return mixed $value changed value
  2023. */
  2024. public function getValue();
  2025. }
  2026. /**
  2027. * RedBean Abstract Query Writer
  2028. *
  2029. * @file RedBean/QueryWriter/AQueryWriter.php
  2030. * @description Quert Writer
  2031. * Represents an abstract Database to RedBean
  2032. * To write a driver for a different database for RedBean
  2033. * Contains a number of functions all implementors can
  2034. * inherit or override.
  2035. * @author Gabor de Mooij and the RedBeanPHP Community
  2036. * @license BSD/GPLv2
  2037. *
  2038. *
  2039. * (c) copyright G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community.
  2040. * This source file is subject to the BSD/GPLv2 License that is bundled
  2041. * with this source code in the file license.txt.
  2042. */
  2043. abstract class RedBean_QueryWriter_AQueryWriter {
  2044. /**
  2045. * Scanned value (scanType)
  2046. * @var type
  2047. */
  2048. protected $svalue;
  2049. /**
  2050. * Supported Column Types.
  2051. * @var array
  2052. */
  2053. public $typeno_sqltype = array();
  2054. /**
  2055. * Holds a reference to the database adapter to be used.
  2056. * @var RedBean_Adapter_DBAdapter
  2057. */
  2058. protected $adapter;
  2059. /**
  2060. * default value to for blank field (passed to PK for auto-increment)
  2061. * @var string
  2062. */
  2063. protected $defaultValue = 'NULL';
  2064. /**
  2065. * character to escape keyword table/column names
  2066. * @var string
  2067. */
  2068. protected $quoteCharacter = '';
  2069. /**
  2070. * Constructor
  2071. * Sets the default Bean Formatter, use parent::__construct() in
  2072. * subclass to achieve this.
  2073. */
  2074. public function __construct() {
  2075. }
  2076. /**
  2077. * Do everything that needs to be done to format a table name.
  2078. *
  2079. * @param string $name of table
  2080. *
  2081. * @return string table name
  2082. */
  2083. public function safeTable($name, $noQuotes = false) {
  2084. $name = $this->check($name);
  2085. if (!$noQuotes) $name = $this->noKW($name);
  2086. return $name;
  2087. }
  2088. /**
  2089. * Do everything that needs to be done to format a column name.
  2090. *
  2091. * @param string $name of column
  2092. *
  2093. * @return string $column name
  2094. */
  2095. public function safeColumn($name, $noQuotes = false) {
  2096. $name = $this->check($name);
  2097. if (!$noQuotes) $name = $this->noKW($name);
  2098. return $name;
  2099. }
  2100. /**
  2101. * Returns the sql that should follow an insert statement.
  2102. *
  2103. * @param string $table name
  2104. *
  2105. * @return string sql
  2106. */
  2107. protected function getInsertSuffix ($table) {
  2108. return '';
  2109. }
  2110. /**
  2111. * Checks table name or column name.
  2112. *
  2113. * @param string $table table string
  2114. *
  2115. * @return string $table escaped string
  2116. */
  2117. protected function check($table) {
  2118. if ($this->quoteCharacter && strpos($table, $this->quoteCharacter)!==false) {
  2119. throw new Redbean_Exception_Security('Illegal chars in table name');
  2120. }
  2121. return $this->adapter->escape($table);
  2122. }
  2123. /**
  2124. * Puts keyword escaping symbols around string.
  2125. *
  2126. * @param string $str keyword
  2127. *
  2128. * @return string $keywordSafeString escaped keyword
  2129. */
  2130. protected function noKW($str) {
  2131. $q = $this->quoteCharacter;
  2132. return $q.$str.$q;
  2133. }
  2134. /**
  2135. * This method adds a column to a table.
  2136. * This methods accepts a type and infers the corresponding table name.
  2137. *
  2138. * @param string $type name of the table
  2139. * @param string $column name of the column
  2140. * @param integer $field data type for field
  2141. *
  2142. * @return void
  2143. *
  2144. */
  2145. public function addColumn( $type, $column, $field ) {
  2146. $table = $type;
  2147. $type = $field;
  2148. $table = $this->safeTable($table);
  2149. $column = $this->safeColumn($column);
  2150. $type = array_key_exists($type, $this->typeno_sqltype) ? $this->typeno_sqltype[$type] : '';
  2151. $sql = "ALTER TABLE $table ADD $column $type ";
  2152. $this->adapter->exec( $sql );
  2153. }
  2154. /**
  2155. * This method updates (or inserts) a record, it takes
  2156. * a table name, a list of update values ( $field => $value ) and an
  2157. * primary key ID (optional). If no primary key ID is provided, an
  2158. * INSERT will take place.
  2159. * Returns the new ID.
  2160. * This methods accepts a type and infers the corresponding table name.
  2161. *
  2162. * @param string $type name of the table to update
  2163. * @param array $updatevalues list of update values
  2164. * @param integer $id optional primary key ID value
  2165. *
  2166. * @return integer $id the primary key ID value of the new record
  2167. */
  2168. public function updateRecord( $type, $updatevalues, $id=null) {
  2169. $table = $type;
  2170. if (!$id) {
  2171. $insertcolumns = $insertvalues = array();
  2172. foreach($updatevalues as $pair) {
  2173. $insertcolumns[] = $pair['property'];
  2174. $insertvalues[] = $pair['value'];
  2175. }
  2176. return $this->insertRecord($table,$insertcolumns,array($insertvalues));
  2177. }
  2178. if ($id && !count($updatevalues)) return $id;
  2179. $table = $this->safeTable($table);
  2180. $sql = "UPDATE $table SET ";
  2181. $p = $v = array();
  2182. foreach($updatevalues as $uv) {
  2183. $p[] = " {$this->safeColumn($uv["property"])} = ? ";
  2184. $v[]=$uv['value'];
  2185. }
  2186. $sql .= implode(',', $p ) .' WHERE id = '.intval($id);
  2187. $this->adapter->exec( $sql, $v );
  2188. return $id;
  2189. }
  2190. /**
  2191. * Inserts a record into the database using a series of insert columns
  2192. * and corresponding insertvalues. Returns the insert id.
  2193. *
  2194. * @param string $table table to perform query on
  2195. * @param array $insertcolumns columns to be inserted
  2196. * @param array $insertvalues values to be inserted
  2197. *
  2198. * @return integer $insertid insert id from driver, new record id
  2199. */
  2200. protected function insertRecord( $table, $insertcolumns, $insertvalues ) {
  2201. $default = $this->defaultValue;
  2202. $suffix = $this->getInsertSuffix($table);
  2203. $table = $this->safeTable($table);
  2204. if (count($insertvalues)>0 && is_array($insertvalues[0]) && count($insertvalues[0])>0) {
  2205. foreach($insertcolumns as $k=>$v) {
  2206. $insertcolumns[$k] = $this->safeColumn($v);
  2207. }
  2208. $insertSQL = "INSERT INTO $table ( id, ".implode(',',$insertcolumns)." ) VALUES
  2209. ( $default, ". implode(',',array_fill(0,count($insertcolumns),' ? '))." ) $suffix";
  2210. foreach($insertvalues as $i=>$insertvalue) {
  2211. $ids[] = $this->adapter->getCell( $insertSQL, $insertvalue, $i );
  2212. }
  2213. $result = count($ids)===1 ? array_pop($ids) : $ids;
  2214. }
  2215. else {
  2216. $result = $this->adapter->getCell( "INSERT INTO $table (id) VALUES($default) $suffix");
  2217. }
  2218. if ($suffix) return $result;
  2219. $last_id = $this->adapter->getInsertID();
  2220. return $last_id;
  2221. }
  2222. /**
  2223. * This selects a record. You provide a
  2224. * collection of conditions using the following format:
  2225. * array( $field1 => array($possibleValue1, $possibleValue2,... $possibleValueN ),
  2226. * ...$fieldN=>array(...));
  2227. * Also, additional SQL can be provided. This SQL snippet will be appended to the
  2228. * query string. If the $delete parameter is set to TRUE instead of selecting the
  2229. * records they will be deleted.
  2230. * This methods accepts a type and infers the corresponding table name.
  2231. *
  2232. * @throws Exception
  2233. * @param string $type type of bean to select records from
  2234. * @param array $cond conditions using the specified format
  2235. * @param string $asql additional sql
  2236. * @param boolean $delete IF TRUE delete records (optional)
  2237. * @param boolean $inverse IF TRUE inverse the selection (optional)
  2238. * @param boolean $all IF TRUE suppress WHERE keyword, omitting WHERE clause
  2239. *
  2240. * @return array $records selected records
  2241. */
  2242. public function selectRecord( $type, $conditions, $addSql=null, $delete=null, $inverse=false, $all=false ) {
  2243. if (!is_array($conditions)) throw new Exception('Conditions must be an array');
  2244. $table = $this->safeTable($type);
  2245. $sqlConditions = array();
  2246. $bindings=array();
  2247. foreach($conditions as $column=>$values) {
  2248. if (!count($values)) continue;
  2249. $sql = $this->safeColumn($column);
  2250. $sql .= ' '.($inverse ? ' NOT ':'').' IN ( ';
  2251. $sql .= implode(',',array_fill(0,count($values),'?')).') ';
  2252. $sqlConditions[] = $sql;
  2253. if (!is_array($values)) $values = array($values);
  2254. foreach($values as $k=>$v) {
  2255. $values[$k]=strval($v);
  2256. }
  2257. $bindings = array_merge($bindings,$values);
  2258. }
  2259. //$addSql can be either just a string or array($sql, $bindings)
  2260. if (is_array($addSql)) {
  2261. if (count($addSql)>1) {
  2262. $bindings = array_merge($bindings,$addSql[1]);
  2263. }
  2264. else {
  2265. $bindings = array();
  2266. }
  2267. $addSql = $addSql[0];
  2268. }
  2269. $sql = '';
  2270. if (count($sqlConditions)>0) {
  2271. $sql = implode(' AND ',$sqlConditions);
  2272. $sql = " WHERE ( $sql ) ";
  2273. if ($addSql) $sql .= " AND $addSql ";
  2274. }
  2275. elseif ($addSql) {
  2276. if ($all) {
  2277. $sql = " $addSql ";
  2278. }
  2279. else {
  2280. $sql = " WHERE $addSql ";
  2281. }
  2282. }
  2283. $sql = (($delete) ? 'DELETE FROM ' : 'SELECT * FROM ').$table.$sql;
  2284. $rows = $this->adapter->get($sql,$bindings);
  2285. return $rows;
  2286. }
  2287. /**
  2288. * This method removes all beans of a certain type.
  2289. * This methods accepts a type and infers the corresponding table name.
  2290. *
  2291. * @param string $type bean type
  2292. *
  2293. * @return void
  2294. */
  2295. public function wipe($type) {
  2296. $table = $type;
  2297. $table = $this->safeTable($table);
  2298. $sql = "TRUNCATE $table ";
  2299. $this->adapter->exec($sql);
  2300. }
  2301. /**
  2302. * Counts rows in a table.
  2303. *
  2304. * @param string $beanType
  2305. *
  2306. * @return integer $numRowsFound
  2307. */
  2308. public function count($beanType) {
  2309. $sql = "SELECT count(*) FROM {$this->safeTable($beanType)} ";
  2310. return (int) $this->adapter->getCell($sql);
  2311. }
  2312. /**
  2313. * This method should add an index to a type and field with name
  2314. * $name.
  2315. * This methods accepts a type and infers the corresponding table name.
  2316. *
  2317. * @param $type type to add index to
  2318. * @param $name name of the new index
  2319. * @param $column field to index
  2320. *
  2321. * @return void
  2322. */
  2323. public function addIndex($type, $name, $column) {
  2324. $table = $type;
  2325. $table = $this->safeTable($table);
  2326. $name = preg_replace('/\W/','',$name);
  2327. $column = $this->safeColumn($column);
  2328. try{ $this->adapter->exec("CREATE INDEX $name ON $table ($column) "); }catch(Exception $e){}
  2329. }
  2330. /**
  2331. * This is a utility service method publicly available.
  2332. * It allows you to check whether you can safely treat an certain value as an integer by
  2333. * comparing an int-valled string representation with a default string casted string representation and
  2334. * a ctype-digit check. It does not take into account numerical limitations (X-bit INT), just that it
  2335. * can be treated like an INT. This is useful for binding parameters to query statements like
  2336. * Query Writers and drivers can do.
  2337. *
  2338. * @static
  2339. *
  2340. * @param string $value string representation of a certain value
  2341. *
  2342. * @return boolean $value boolean result of analysis
  2343. */
  2344. public static function canBeTreatedAsInt( $value ) {
  2345. return (boolean) (ctype_digit(strval($value)) && strval($value)===strval(intval($value)));
  2346. }
  2347. /**
  2348. * This method adds a foreign key from type and field to
  2349. * target type and target field.
  2350. * The foreign key is created without an action. On delete/update
  2351. * no action will be triggered. The FK is only used to allow database
  2352. * tools to generate pretty diagrams and to make it easy to add actions
  2353. * later on.
  2354. * This methods accepts a type and infers the corresponding table name.
  2355. *
  2356. *
  2357. * @param string $type type that will have a foreign key field
  2358. * @param string $targetType points to this type
  2359. * @param string $field field that contains the foreign key value
  2360. * @param string $targetField field where the fk points to
  2361. *
  2362. * @return void
  2363. */
  2364. public function addFK( $type, $targetType, $field, $targetField, $isDependent = false) {
  2365. $table = $this->safeTable($type);
  2366. $tableNoQ = $this->safeTable($type,true);
  2367. $targetTable = $this->safeTable($targetType);
  2368. $column = $this->safeColumn($field);
  2369. $columnNoQ = $this->safeColumn($field,true);
  2370. $targetColumn = $this->safeColumn($targetField);
  2371. $targetColumnNoQ = $this->safeColumn($targetField,true);
  2372. $db = $this->adapter->getCell('select database()');
  2373. $fkName = 'fk_'.$tableNoQ.'_'.$columnNoQ.'_'.$targetColumnNoQ.($isDependent ? '_casc':'');
  2374. $cName = 'cons_'.$fkName;
  2375. $cfks = $this->adapter->getCell("
  2376. SELECT CONSTRAINT_NAME
  2377. FROM information_schema.KEY_COLUMN_USAGE
  2378. WHERE TABLE_SCHEMA ='$db' AND TABLE_NAME = '$tableNoQ' AND COLUMN_NAME = '$columnNoQ' AND
  2379. CONSTRAINT_NAME <>'PRIMARY' AND REFERENCED_TABLE_NAME is not null
  2380. ");
  2381. $flagAddKey = false;
  2382. try{
  2383. //No keys
  2384. if (!$cfks) {
  2385. $flagAddKey = true; //go get a new key
  2386. }
  2387. //has fk, but different setting, --remove
  2388. if ($cfks && $cfks!=$cName) {
  2389. $this->adapter->exec("ALTER TABLE $table DROP FOREIGN KEY $cfks ");
  2390. $flagAddKey = true; //go get a new key.
  2391. }
  2392. if ($flagAddKey) {
  2393. $this->adapter->exec("ALTER TABLE $table
  2394. ADD CONSTRAINT $cName FOREIGN KEY $fkName ( $column ) REFERENCES $targetTable (
  2395. $targetColumn) ON DELETE ".($isDependent ? 'CASCADE':'SET NULL').' ON UPDATE SET NULL ;');
  2396. }
  2397. }
  2398. catch(Exception $e) { } //Failure of fk-constraints is not a problem
  2399. }
  2400. /**
  2401. * Returns the format for link tables.
  2402. * Given an array containing two type names this method returns the
  2403. * name of the link table to be used to store and retrieve
  2404. * association records.
  2405. *
  2406. * @param array $types two types array($type1,$type2)
  2407. *
  2408. * @return string $linktable name of the link table
  2409. */
  2410. public static function getAssocTableFormat($types) {
  2411. sort($types);
  2412. return ( implode('_', $types) );
  2413. }
  2414. /**
  2415. * Adds a constraint. If one of the beans gets trashed
  2416. * the other, related bean should be removed as well.
  2417. *
  2418. * @param RedBean_OODBBean $bean1 first bean
  2419. * @param RedBean_OODBBean $bean2 second bean
  2420. * @param bool $dontCache by default we use a cache, TRUE = NO CACHING (optional)
  2421. *
  2422. * @return void
  2423. */
  2424. public function addConstraint( RedBean_OODBBean $bean1, RedBean_OODBBean $bean2) {
  2425. $table1 = $bean1->getMeta('type');
  2426. $table2 = $bean2->getMeta('type');
  2427. $writer = $this;
  2428. $adapter = $this->adapter;
  2429. $table = RedBean_QueryWriter_AQueryWriter::getAssocTableFormat( array( $table1,$table2) );
  2430. $property1 = $bean1->getMeta('type') . '_id';
  2431. $property2 = $bean2->getMeta('type') . '_id';
  2432. if ($property1==$property2) $property2 = $bean2->getMeta('type').'2_id';
  2433. $table = $adapter->escape($table);
  2434. $table1 = $adapter->escape($table1);
  2435. $table2 = $adapter->escape($table2);
  2436. $property1 = $adapter->escape($property1);
  2437. $property2 = $adapter->escape($property2);
  2438. //Dispatch to right method
  2439. return $this->constrain($table, $table1, $table2, $property1, $property2);
  2440. }
  2441. /**
  2442. * Checks whether a value starts with zeros. In this case
  2443. * the value should probably be stored using a text datatype instead of a
  2444. * numerical type in order to preserve the zeros.
  2445. *
  2446. * @param string $value value to be checked.
  2447. */
  2448. protected function startsWithZeros($value) {
  2449. $value = strval($value);
  2450. if (strlen($value)>1 && strpos($value,'0')===0 && strpos($value,'0.')!==0) {
  2451. return true;
  2452. }
  2453. else {
  2454. return false;
  2455. }
  2456. }
  2457. /**
  2458. * Returns a modified value from ScanType.
  2459. * Used for special types.
  2460. *
  2461. * @return mixed $value changed value
  2462. */
  2463. public function getValue(){
  2464. return $this->svalue;
  2465. }
  2466. }
  2467. /**
  2468. * RedBean MySQLWriter
  2469. *
  2470. * @file RedBean/QueryWriter/MySQL.php
  2471. * @description Represents a MySQL Database to RedBean
  2472. * To write a driver for a different database for RedBean
  2473. * you should only have to change this file.
  2474. * @author Gabor de Mooij and the RedBeanPHP Community
  2475. * @license BSD/GPLv2
  2476. *
  2477. *
  2478. * (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community.
  2479. * This source file is subject to the BSD/GPLv2 License that is bundled
  2480. * with this source code in the file license.txt.
  2481. */
  2482. class RedBean_QueryWriter_MySQL extends RedBean_QueryWriter_AQueryWriter implements RedBean_QueryWriter {
  2483. /**
  2484. * Here we describe the datatypes that RedBean
  2485. * Uses internally. If you write a QueryWriter for
  2486. * RedBean you should provide a list of types like this.
  2487. */
  2488. /**
  2489. * DATA TYPE
  2490. * Boolean Data type
  2491. * @var integer
  2492. */
  2493. const C_DATATYPE_BOOL = 0;
  2494. /**
  2495. *
  2496. * DATA TYPE
  2497. * Unsigned 8BIT Integer
  2498. * @var integer
  2499. */
  2500. const C_DATATYPE_UINT8 = 1;
  2501. /**
  2502. *
  2503. * DATA TYPE
  2504. * Unsigned 32BIT Integer
  2505. * @var integer
  2506. */
  2507. const C_DATATYPE_UINT32 = 2;
  2508. /**
  2509. * DATA TYPE
  2510. * Double precision floating point number and
  2511. * negative numbers.
  2512. * @var integer
  2513. */
  2514. const C_DATATYPE_DOUBLE = 3;
  2515. /**
  2516. * DATA TYPE
  2517. * Standard Text column (like varchar255)
  2518. * At least 8BIT character support.
  2519. * @var integer
  2520. */
  2521. const C_DATATYPE_TEXT8 = 4;
  2522. /**
  2523. * DATA TYPE
  2524. * Long text column (16BIT)
  2525. * @var integer
  2526. */
  2527. const C_DATATYPE_TEXT16 = 5;
  2528. /**
  2529. *
  2530. * DATA TYPE
  2531. * 32BIT long textfield (number of characters can be as high as 32BIT) Data type
  2532. * This is the biggest column that RedBean supports. If possible you may write
  2533. * an implementation that stores even bigger values.
  2534. * @var integer
  2535. */
  2536. const C_DATATYPE_TEXT32 = 6;
  2537. /**
  2538. * Special type date for storing date values: YYYY-MM-DD
  2539. * @var integer
  2540. */
  2541. const C_DATATYPE_SPECIAL_DATE = 80;
  2542. /**
  2543. * Special type datetime for store date-time values: YYYY-MM-DD HH:II:SS
  2544. * @var integer
  2545. */
  2546. const C_DATATYPE_SPECIAL_DATETIME = 81;
  2547. /**
  2548. *
  2549. * DATA TYPE
  2550. * Specified. This means the developer or DBA
  2551. * has altered the column to a different type not
  2552. * recognized by RedBean. This high number makes sure
  2553. * it will not be converted back to another type by accident.
  2554. * @var integer
  2555. */
  2556. const C_DATATYPE_SPECIFIED = 99;
  2557. /**
  2558. * Spatial types
  2559. * @var integer
  2560. */
  2561. const C_DATATYPE_SPECIAL_POINT = 100;
  2562. const C_DATATYPE_SPECIAL_LINESTRING = 101;
  2563. const C_DATATYPE_SPECIAL_GEOMETRY = 102;
  2564. const C_DATATYPE_SPECIAL_POLYGON = 103;
  2565. const C_DATATYPE_SPECIAL_MULTIPOINT = 104;
  2566. const C_DATATYPE_SPECIAL_MULTIPOLYGON = 105;
  2567. const C_DATATYPE_SPECIAL_GEOMETRYCOLLECTION = 106;
  2568. /**
  2569. * Holds the RedBean Database Adapter.
  2570. * @var RedBean_Adapter_DBAdapter
  2571. */
  2572. protected $adapter;
  2573. /**
  2574. * character to escape keyword table/column names
  2575. * @var string
  2576. */
  2577. protected $quoteCharacter = '`';
  2578. /**
  2579. * Constructor.
  2580. * The Query Writer Constructor also sets up the database.
  2581. *
  2582. * @param RedBean_Adapter_DBAdapter $adapter adapter
  2583. *
  2584. */
  2585. public function __construct( RedBean_Adapter $adapter ) {
  2586. $this->typeno_sqltype = array(
  2587. RedBean_QueryWriter_MySQL::C_DATATYPE_BOOL=>" SET('1') ",
  2588. RedBean_QueryWriter_MySQL::C_DATATYPE_UINT8=>' TINYINT(3) UNSIGNED ',
  2589. RedBean_QueryWriter_MySQL::C_DATATYPE_UINT32=>' INT(11) UNSIGNED ',
  2590. RedBean_QueryWriter_MySQL::C_DATATYPE_DOUBLE=>' DOUBLE ',
  2591. RedBean_QueryWriter_MySQL::C_DATATYPE_TEXT8=>' VARCHAR(255) ',
  2592. RedBean_QueryWriter_MySQL::C_DATATYPE_TEXT16=>' TEXT ',
  2593. RedBean_QueryWriter_MySQL::C_DATATYPE_TEXT32=>' LONGTEXT ',
  2594. RedBean_QueryWriter_MySQL::C_DATATYPE_SPECIAL_DATE=>' DATE ',
  2595. RedBean_QueryWriter_MySQL::C_DATATYPE_SPECIAL_DATETIME=>' DATETIME ',
  2596. RedBean_QueryWriter_MySQL::C_DATATYPE_SPECIAL_POINT=>' POINT ',
  2597. RedBean_QueryWriter_MySQL::C_DATATYPE_SPECIAL_LINESTRING=>' LINESTRING ',
  2598. RedBean_QueryWriter_MySQL::C_DATATYPE_SPECIAL_GEOMETRY=>' GEOMETRY ',
  2599. RedBean_QueryWriter_MySQL::C_DATATYPE_SPECIAL_POLYGON=>' POLYGON ',
  2600. RedBean_QueryWriter_MySQL::C_DATATYPE_SPECIAL_MULTIPOINT=>' MULTIPOINT ',
  2601. RedBean_QueryWriter_MySQL::C_DATATYPE_SPECIAL_MULTIPOLYGON=>' MULTIPOLYGON ',
  2602. RedBean_QueryWriter_MySQL::C_DATATYPE_SPECIAL_GEOMETRYCOLLECTION=>' GEOMETRYCOLLECTION ',
  2603. );
  2604. $this->sqltype_typeno = array();
  2605. foreach($this->typeno_sqltype as $k=>$v)
  2606. $this->sqltype_typeno[trim(strtolower($v))]=$k;
  2607. $this->adapter = $adapter;
  2608. parent::__construct();
  2609. }
  2610. /**
  2611. * This method returns the datatype to be used for primary key IDS and
  2612. * foreign keys. Returns one if the data type constants.
  2613. *
  2614. * @return integer $const data type to be used for IDS.
  2615. */
  2616. public function getTypeForID() {
  2617. return self::C_DATATYPE_UINT32;
  2618. }
  2619. /**
  2620. * Returns all tables in the database.
  2621. *
  2622. * @return array $tables tables
  2623. */
  2624. public function getTables() {
  2625. return $this->adapter->getCol( 'show tables' );
  2626. }
  2627. /**
  2628. * Creates an empty, column-less table for a bean based on it's type.
  2629. * This function creates an empty table for a bean. It uses the
  2630. * safeTable() function to convert the type name to a table name.
  2631. *
  2632. * @param string $table type of bean you want to create a table for
  2633. *
  2634. * @return void
  2635. */
  2636. public function createTable( $table ) {
  2637. $table = $this->safeTable($table);
  2638. $sql = " CREATE TABLE $table (
  2639. id INT( 11 ) UNSIGNED NOT NULL AUTO_INCREMENT ,
  2640. PRIMARY KEY ( id )
  2641. ) ENGINE = InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci ";
  2642. $this->adapter->exec( $sql );
  2643. }
  2644. /**
  2645. * Returns an array containing the column names of the specified table.
  2646. *
  2647. * @param string $table table
  2648. *
  2649. * @return array $columns columns
  2650. */
  2651. public function getColumns( $table ) {
  2652. $table = $this->safeTable($table);
  2653. $columnsRaw = $this->adapter->get("DESCRIBE $table");
  2654. foreach($columnsRaw as $r) {
  2655. $columns[$r['Field']]=$r['Type'];
  2656. }
  2657. return $columns;
  2658. }
  2659. /**
  2660. * Returns the MySQL Column Type Code (integer) that corresponds
  2661. * to the given value type.
  2662. *
  2663. * @param string $value value
  2664. *
  2665. * @return integer $type type
  2666. */
  2667. public function scanType( $value, $flagSpecial=false ) {
  2668. $this->svalue = $value;
  2669. if (is_null($value)) {
  2670. return RedBean_QueryWriter_MySQL::C_DATATYPE_BOOL;
  2671. }
  2672. if ($flagSpecial) {
  2673. if (strpos($value,'POINT(')===0) {
  2674. $this->svalue = $this->adapter->getCell('SELECT GeomFromText(?)',array($value));
  2675. return RedBean_QueryWriter_MySQL::C_DATATYPE_SPECIAL_POINT;
  2676. }
  2677. if (strpos($value,'LINESTRING(')===0) {
  2678. $this->svalue = $this->adapter->getCell('SELECT GeomFromText(?)',array($value));
  2679. return RedBean_QueryWriter_MySQL::C_DATATYPE_SPECIAL_LINESTRING;
  2680. }
  2681. if (strpos($value,'POLYGON(')===0) {
  2682. $this->svalue = $this->adapter->getCell('SELECT GeomFromText(?)',array($value));
  2683. return RedBean_QueryWriter_MySQL::C_DATATYPE_SPECIAL_POLYGON;
  2684. }
  2685. if (strpos($value,'MULTIPOINT(')===0) {
  2686. $this->svalue = $this->adapter->getCell('SELECT GeomFromText(?)',array($value));
  2687. return RedBean_QueryWriter_MySQL::C_DATATYPE_SPECIAL_MULTIPOINT;
  2688. }
  2689. if (preg_match('/^\d{4}\-\d\d-\d\d$/',$value)) {
  2690. return RedBean_QueryWriter_MySQL::C_DATATYPE_SPECIAL_DATE;
  2691. }
  2692. if (preg_match('/^\d{4}\-\d\d-\d\d\s\d\d:\d\d:\d\d$/',$value)) {
  2693. return RedBean_QueryWriter_MySQL::C_DATATYPE_SPECIAL_DATETIME;
  2694. }
  2695. }
  2696. $value = strval($value);
  2697. if (!$this->startsWithZeros($value)) {
  2698. if ($value=='1' || $value=='') {
  2699. return RedBean_QueryWriter_MySQL::C_DATATYPE_BOOL;
  2700. }
  2701. if (is_numeric($value) && (floor($value)==$value) && $value >= 0 && $value <= 255 ) {
  2702. return RedBean_QueryWriter_MySQL::C_DATATYPE_UINT8;
  2703. }
  2704. if (is_numeric($value) && (floor($value)==$value) && $value >= 0 && $value <= 4294967295 ) {
  2705. return RedBean_QueryWriter_MySQL::C_DATATYPE_UINT32;
  2706. }
  2707. if (is_numeric($value)) {
  2708. return RedBean_QueryWriter_MySQL::C_DATATYPE_DOUBLE;
  2709. }
  2710. }
  2711. if (strlen($value) <= 255) {
  2712. return RedBean_QueryWriter_MySQL::C_DATATYPE_TEXT8;
  2713. }
  2714. if (strlen($value) <= 65535) {
  2715. return RedBean_QueryWriter_MySQL::C_DATATYPE_TEXT16;
  2716. }
  2717. return RedBean_QueryWriter_MySQL::C_DATATYPE_TEXT32;
  2718. }
  2719. /**
  2720. * Returns the Type Code for a Column Description.
  2721. * Given an SQL column description this method will return the corresponding
  2722. * code for the writer. If the include specials flag is set it will also
  2723. * return codes for special columns. Otherwise special columns will be identified
  2724. * as specified columns.
  2725. *
  2726. * @param string $typedescription description
  2727. * @param boolean $includeSpecials whether you want to get codes for special columns as well
  2728. *
  2729. * @return integer $typecode code
  2730. */
  2731. public function code( $typedescription, $includeSpecials = false ) {
  2732. $r = ((isset($this->sqltype_typeno[$typedescription])) ? $this->sqltype_typeno[$typedescription] : self::C_DATATYPE_SPECIFIED);
  2733. if ($includeSpecials) return $r;
  2734. if ($r > self::C_DATATYPE_SPECIFIED) return self::C_DATATYPE_SPECIFIED;
  2735. return $r;
  2736. }
  2737. /**
  2738. * This method upgrades the column to the specified data type.
  2739. * This methods accepts a type and infers the corresponding table name.
  2740. *
  2741. * @param string $type type / table that needs to be adjusted
  2742. * @param string $column column that needs to be altered
  2743. * @param integer $datatype target data type
  2744. *
  2745. * @return void
  2746. */
  2747. public function widenColumn( $type, $column, $datatype ) {
  2748. $table = $type;
  2749. $type = $datatype;
  2750. $table = $this->safeTable($table);
  2751. $column = $this->safeColumn($column);
  2752. $newtype = array_key_exists($type, $this->typeno_sqltype) ? $this->typeno_sqltype[$type] : '';
  2753. $changecolumnSQL = "ALTER TABLE $table CHANGE $column $column $newtype ";
  2754. $this->adapter->exec( $changecolumnSQL );
  2755. }
  2756. /**
  2757. * Adds a Unique index constrain to the table.
  2758. *
  2759. * @param string $table table
  2760. * @param string $col1 column
  2761. * @param string $col2 column
  2762. *
  2763. * @return void
  2764. */
  2765. public function addUniqueIndex( $table,$columns ) {
  2766. $table = $this->safeTable($table);
  2767. sort($columns); //else we get multiple indexes due to order-effects
  2768. foreach($columns as $k=>$v) {
  2769. $columns[$k]= $this->safeColumn($v);
  2770. }
  2771. $r = $this->adapter->get("SHOW INDEX FROM $table");
  2772. $name = 'UQ_'.sha1(implode(',',$columns));
  2773. if ($r) {
  2774. foreach($r as $i) {
  2775. if ($i['Key_name']== $name) {
  2776. return;
  2777. }
  2778. }
  2779. }
  2780. $sql = "ALTER IGNORE TABLE $table
  2781. ADD UNIQUE INDEX $name (".implode(',',$columns).")";
  2782. $this->adapter->exec($sql);
  2783. }
  2784. /**
  2785. * Tests whether a given SQL state is in the list of states.
  2786. *
  2787. * @param string $state code
  2788. * @param array $list array of sql states
  2789. *
  2790. * @return boolean $yesno occurs in list
  2791. */
  2792. public function sqlStateIn($state, $list) {
  2793. $stateMap = array(
  2794. '42S02'=>RedBean_QueryWriter::C_SQLSTATE_NO_SUCH_TABLE,
  2795. '42S22'=>RedBean_QueryWriter::C_SQLSTATE_NO_SUCH_COLUMN,
  2796. '23000'=>RedBean_QueryWriter::C_SQLSTATE_INTEGRITY_CONSTRAINT_VIOLATION
  2797. );
  2798. return in_array((isset($stateMap[$state]) ? $stateMap[$state] : '0'),$list);
  2799. }
  2800. /**
  2801. * Add the constraints for a specific database driver: MySQL.
  2802. * @todo Too many arguments; find a way to solve this in a neater way.
  2803. *
  2804. * @param string $table table
  2805. * @param string $table1 table1
  2806. * @param string $table2 table2
  2807. * @param string $property1 property1
  2808. * @param string $property2 property2
  2809. *
  2810. * @return boolean $succes whether the constraint has been applied
  2811. */
  2812. protected function constrain($table, $table1, $table2, $property1, $property2) {
  2813. try{
  2814. $db = $this->adapter->getCell('select database()');
  2815. $fks = $this->adapter->getCell("
  2816. SELECT count(*)
  2817. FROM information_schema.KEY_COLUMN_USAGE
  2818. WHERE TABLE_SCHEMA = ? AND TABLE_NAME = ? AND
  2819. CONSTRAINT_NAME <>'PRIMARY' AND REFERENCED_TABLE_NAME is not null
  2820. ",array($db,$table));
  2821. //already foreign keys added in this association table
  2822. if ($fks>0) return false;
  2823. $columns = $this->getColumns($table);
  2824. if ($this->code($columns[$property1])!==RedBean_QueryWriter_MySQL::C_DATATYPE_UINT32) {
  2825. $this->widenColumn($table, $property1, RedBean_QueryWriter_MySQL::C_DATATYPE_UINT32);
  2826. }
  2827. if ($this->code($columns[$property2])!==RedBean_QueryWriter_MySQL::C_DATATYPE_UINT32) {
  2828. $this->widenColumn($table, $property2, RedBean_QueryWriter_MySQL::C_DATATYPE_UINT32);
  2829. }
  2830. $sql = "
  2831. ALTER TABLE ".$this->noKW($table)."
  2832. ADD FOREIGN KEY($property1) references `$table1`(id) ON DELETE CASCADE;
  2833. ";
  2834. $this->adapter->exec( $sql );
  2835. $sql ="
  2836. ALTER TABLE ".$this->noKW($table)."
  2837. ADD FOREIGN KEY($property2) references `$table2`(id) ON DELETE CASCADE
  2838. ";
  2839. $this->adapter->exec( $sql );
  2840. return true;
  2841. } catch(Exception $e){ return false; }
  2842. }
  2843. /**
  2844. * Drops all tables in database
  2845. */
  2846. public function wipeAll() {
  2847. $this->adapter->exec('SET FOREIGN_KEY_CHECKS=0;');
  2848. foreach($this->getTables() as $t) {
  2849. try{
  2850. $this->adapter->exec("drop table if exists`$t`");
  2851. }
  2852. catch(Exception $e){}
  2853. try{
  2854. $this->adapter->exec("drop view if exists`$t`");
  2855. }
  2856. catch(Exception $e){}
  2857. }
  2858. $this->adapter->exec('SET FOREIGN_KEY_CHECKS=1;');
  2859. }
  2860. }
  2861. /**
  2862. * RedBean SQLiteWriter with support for SQLite types
  2863. *
  2864. * @file RedBean/QueryWriter/SQLiteT.php
  2865. * @description Represents a SQLite Database to RedBean
  2866. * To write a driver for a different database for RedBean
  2867. * you should only have to change this file.
  2868. * @author Gabor de Mooij and the RedBeanPHP Community
  2869. * @license BSD/GPLv2
  2870. *
  2871. * (c) copyright G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community.
  2872. * This source file is subject to the BSD/GPLv2 License that is bundled
  2873. * with this source code in the file license.txt.
  2874. */
  2875. class RedBean_QueryWriter_SQLiteT extends RedBean_QueryWriter_AQueryWriter implements RedBean_QueryWriter {
  2876. /**
  2877. *
  2878. * @var RedBean_Adapter_DBAdapter
  2879. * Holds database adapter
  2880. */
  2881. protected $adapter;
  2882. /**
  2883. * @var string
  2884. * character to escape keyword table/column names
  2885. */
  2886. protected $quoteCharacter = '`';
  2887. /**
  2888. * Here we describe the datatypes that RedBean
  2889. * Uses internally. If you write a QueryWriter for
  2890. * RedBean you should provide a list of types like this.
  2891. */
  2892. /**
  2893. * DATA TYPE
  2894. * Integer Data type
  2895. * @var integer
  2896. */
  2897. const C_DATATYPE_INTEGER = 0;
  2898. /**
  2899. * DATA TYPE
  2900. * Numeric Data type (for REAL and date/time)
  2901. * @var integer
  2902. */
  2903. const C_DATATYPE_NUMERIC = 1;
  2904. /**
  2905. * DATA TYPE
  2906. * Text type
  2907. * @var integer
  2908. */
  2909. const C_DATATYPE_TEXT = 2;
  2910. /**
  2911. * DATA TYPE
  2912. * Specified. This means the developer or DBA
  2913. * has altered the column to a different type not
  2914. * recognized by RedBean. This high number makes sure
  2915. * it will not be converted back to another type by accident.
  2916. * @var integer
  2917. */
  2918. const C_DATATYPE_SPECIFIED = 99;
  2919. /**
  2920. * Constructor
  2921. * The Query Writer Constructor also sets up the database
  2922. *
  2923. * @param RedBean_Adapter_DBAdapter $adapter adapter
  2924. */
  2925. public function __construct( RedBean_Adapter $adapter ) {
  2926. $this->typeno_sqltype = array(
  2927. RedBean_QueryWriter_SQLiteT::C_DATATYPE_INTEGER=>'INTEGER',
  2928. RedBean_QueryWriter_SQLiteT::C_DATATYPE_NUMERIC=>'NUMERIC',
  2929. RedBean_QueryWriter_SQLiteT::C_DATATYPE_TEXT=>'TEXT',
  2930. );
  2931. $this->sqltype_typeno = array();
  2932. foreach($this->typeno_sqltype as $k=>$v)
  2933. $this->sqltype_typeno[$v]=$k;
  2934. $this->adapter = $adapter;
  2935. parent::__construct($adapter);
  2936. }
  2937. /**
  2938. * This method returns the datatype to be used for primary key IDS and
  2939. * foreign keys. Returns one if the data type constants.
  2940. *
  2941. * @return integer $const data type to be used for IDS.
  2942. */
  2943. public function getTypeForID() {
  2944. return self::C_DATATYPE_INTEGER;
  2945. }
  2946. /**
  2947. * Returns the MySQL Column Type Code (integer) that corresponds
  2948. * to the given value type.
  2949. *
  2950. * @param string $value value
  2951. *
  2952. * @return integer $type type
  2953. */
  2954. public function scanType( $value, $flagSpecial=false ) {
  2955. $this->svalue=$value;
  2956. if ($value===false) return self::C_DATATYPE_INTEGER;
  2957. if ($value===null) return self::C_DATATYPE_INTEGER; //for fks
  2958. if ($this->startsWithZeros($value)) return self::C_DATATYPE_TEXT;
  2959. if (is_numeric($value) && (intval($value)==$value) && $value<2147483648) return self::C_DATATYPE_INTEGER;
  2960. if ((is_numeric($value) && $value < 2147483648)
  2961. || preg_match('/\d{4}\-\d\d\-\d\d/',$value)
  2962. || preg_match('/\d{4}\-\d\d\-\d\d\s\d\d:\d\d:\d\d/',$value)
  2963. ) {
  2964. return self::C_DATATYPE_NUMERIC;
  2965. }
  2966. return self::C_DATATYPE_TEXT;
  2967. }
  2968. /**
  2969. * Adds a column of a given type to a table
  2970. *
  2971. * @param string $table table
  2972. * @param string $column column
  2973. * @param integer $type type
  2974. */
  2975. public function addColumn( $table, $column, $type) {
  2976. $column = $this->check($column);
  2977. $table = $this->check($table);
  2978. $type=$this->typeno_sqltype[$type];
  2979. $sql = "ALTER TABLE `$table` ADD `$column` $type ";
  2980. $this->adapter->exec( $sql );
  2981. }
  2982. /**
  2983. * Returns the Type Code for a Column Description.
  2984. * Given an SQL column description this method will return the corresponding
  2985. * code for the writer. If the include specials flag is set it will also
  2986. * return codes for special columns. Otherwise special columns will be identified
  2987. * as specified columns.
  2988. *
  2989. * @param string $typedescription description
  2990. * @param boolean $includeSpecials whether you want to get codes for special columns as well
  2991. *
  2992. * @return integer $typecode code
  2993. */
  2994. public function code( $typedescription, $includeSpecials = false ) {
  2995. $r = ((isset($this->sqltype_typeno[$typedescription])) ? $this->sqltype_typeno[$typedescription] : 99);
  2996. if ($includeSpecials) return $r;
  2997. if ($r > self::C_DATATYPE_SPECIFIED) return self::C_DATATYPE_SPECIFIED;
  2998. return $r;
  2999. }
  3000. /**
  3001. * Quote Items, to prevent issues with reserved words.
  3002. *
  3003. * @param array $items items to quote
  3004. *
  3005. * @return $quotedfItems quoted items
  3006. */
  3007. private function quote( $items ) {
  3008. foreach($items as $k=>$item) {
  3009. $items[$k]=$this->noKW($item);
  3010. }
  3011. return $items;
  3012. }
  3013. /**
  3014. * This method upgrades the column to the specified data type.
  3015. * This methods accepts a type and infers the corresponding table name.
  3016. *
  3017. * @param string $type type / table that needs to be adjusted
  3018. * @param string $column column that needs to be altered
  3019. * @param integer $datatype target data type
  3020. *
  3021. * @return void
  3022. */
  3023. public function widenColumn( $type, $column, $datatype ) {
  3024. $table = $this->safeTable($type,true);
  3025. $column = $this->safeColumn($column,true);
  3026. $newtype = $this->typeno_sqltype[$datatype];
  3027. $oldColumns = $this->getColumns($type);
  3028. $oldColumnNames = $this->quote(array_keys($oldColumns));
  3029. $newTableDefStr='';
  3030. foreach($oldColumns as $oldName=>$oldType) {
  3031. if ($oldName != 'id') {
  3032. if ($oldName!=$column) {
  3033. $newTableDefStr .= ",`$oldName` $oldType";
  3034. }
  3035. else {
  3036. $newTableDefStr .= ",`$oldName` $newtype";
  3037. }
  3038. }
  3039. }
  3040. $q = array();
  3041. $q[] = "DROP TABLE IF EXISTS tmp_backup;";
  3042. $q[] = "CREATE TEMPORARY TABLE tmp_backup(".implode(",",$oldColumnNames).");";
  3043. $q[] = "INSERT INTO tmp_backup SELECT * FROM `$table`;";
  3044. $q[] = "DROP TABLE `$table`;";
  3045. $q[] = "CREATE TABLE `$table` ( `id` INTEGER PRIMARY KEY AUTOINCREMENT $newTableDefStr );";
  3046. $q[] = "INSERT INTO `$table` SELECT * FROM tmp_backup;";
  3047. $q[] = "DROP TABLE tmp_backup;";
  3048. foreach($q as $sq) {
  3049. $this->adapter->exec($sq);
  3050. }
  3051. }
  3052. /**
  3053. * Returns all tables in the database
  3054. *
  3055. * @return array $tables tables
  3056. */
  3057. public function getTables() {
  3058. return $this->adapter->getCol( "SELECT name FROM sqlite_master
  3059. WHERE type='table' AND name!='sqlite_sequence';" );
  3060. }
  3061. /**
  3062. * Creates an empty, column-less table for a bean.
  3063. *
  3064. * @param string $table table
  3065. */
  3066. public function createTable( $table ) {
  3067. $table = $this->safeTable($table);
  3068. $sql = "CREATE TABLE $table ( id INTEGER PRIMARY KEY AUTOINCREMENT ) ";
  3069. $this->adapter->exec( $sql );
  3070. }
  3071. /**
  3072. * Returns an array containing the column names of the specified table.
  3073. *
  3074. * @param string $table table
  3075. *
  3076. * @return array $columns columns
  3077. */
  3078. public function getColumns( $table ) {
  3079. $table = $this->safeTable($table, true);
  3080. $columnsRaw = $this->adapter->get("PRAGMA table_info('$table')");
  3081. $columns = array();
  3082. foreach($columnsRaw as $r) {
  3083. $columns[$r['name']]=$r['type'];
  3084. }
  3085. return $columns;
  3086. }
  3087. /**
  3088. * Adds a Unique index constrain to the table.
  3089. *
  3090. * @param string $table table
  3091. * @param string $column1 first column
  3092. * @param string $column2 second column
  3093. *
  3094. * @return void
  3095. */
  3096. public function addUniqueIndex( $table,$columns ) {
  3097. $table = $this->safeTable($table);
  3098. $name = 'UQ_'.sha1(implode(',',$columns));
  3099. $sql = "CREATE UNIQUE INDEX IF NOT EXISTS $name ON $table (".implode(',',$columns).")";
  3100. $this->adapter->exec($sql);
  3101. }
  3102. /**
  3103. * Given an Database Specific SQLState and a list of QueryWriter
  3104. * Standard SQL States this function converts the raw SQL state to a
  3105. * database agnostic ANSI-92 SQL states and checks if the given state
  3106. * is in the list of agnostic states.
  3107. *
  3108. * @param string $state state
  3109. * @param array $list list of states
  3110. *
  3111. * @return boolean $isInArray whether state is in list
  3112. */
  3113. public function sqlStateIn($state, $list) {
  3114. $stateMap = array(
  3115. 'HY000'=>RedBean_QueryWriter::C_SQLSTATE_NO_SUCH_TABLE,
  3116. '23000'=>RedBean_QueryWriter::C_SQLSTATE_INTEGRITY_CONSTRAINT_VIOLATION
  3117. );
  3118. return in_array((isset($stateMap[$state]) ? $stateMap[$state] : '0'),$list);
  3119. }
  3120. /**
  3121. * Counts rows in a table.
  3122. * Uses SQLite optimization for deleting all records (i.e. no WHERE)
  3123. *
  3124. * @param string $beanType
  3125. *
  3126. * @return integer $numRowsFound
  3127. */
  3128. public function wipe($type) {
  3129. $table = $this->safeTable($type);
  3130. $this->adapter->exec("DELETE FROM $table");
  3131. }
  3132. /**
  3133. * Adds a foreign key to a type
  3134. *
  3135. * @param string $type type you want to modify table of
  3136. * @param string $targetType target type
  3137. * @param string $field field of the type that needs to get the fk
  3138. * @param string $targetField field where the fk needs to point to
  3139. * @param boolean $isDep whether this field is dependent on it's referenced record
  3140. *
  3141. * @return bool $success whether an FK has been added
  3142. */
  3143. public function addFK( $type, $targetType, $field, $targetField, $isDep=false) {
  3144. return $this->buildFK($type, $targetType, $field, $targetField, $isDep);
  3145. }
  3146. /**
  3147. * Adds a foreign key to a type
  3148. *
  3149. * @param string $type type you want to modify table of
  3150. * @param string $targetType target type
  3151. * @param string $field field of the type that needs to get the fk
  3152. * @param string $targetField field where the fk needs to point to
  3153. * @param integer $buildopt 0 = NO ACTION, 1 = ON DELETE CASCADE
  3154. *
  3155. * @return bool $success whether an FK has been added
  3156. */
  3157. protected function buildFK($type, $targetType, $field, $targetField,$constraint=false) {
  3158. try{
  3159. $consSQL = ($constraint ? 'CASCADE' : 'SET NULL');
  3160. $table = $this->safeTable($type,true);
  3161. $targetTable = $this->safeTable($targetType,true);
  3162. $field = $this->safeColumn($field,true);
  3163. $targetField = $this->safeColumn($targetField,true);
  3164. $oldColumns = $this->getColumns($type);
  3165. $oldColumnNames = $this->quote(array_keys($oldColumns));
  3166. $newTableDefStr='';
  3167. foreach($oldColumns as $oldName=>$oldType) {
  3168. if ($oldName != 'id') {
  3169. $newTableDefStr .= ",`$oldName` $oldType";
  3170. }
  3171. }
  3172. //retrieve old foreign keys
  3173. $sqlGetOldFKS = "PRAGMA foreign_key_list('$table'); ";
  3174. $oldFKs = $this->adapter->get($sqlGetOldFKS);
  3175. $restoreFKSQLSnippets = "";
  3176. foreach($oldFKs as $oldFKInfo) {
  3177. if ($oldFKInfo['from']==$field && $oldFKInfo['on_delete']==$consSQL) {
  3178. //this field already has a FK.
  3179. return false;
  3180. }
  3181. if ($oldFKInfo['from']==$field && $oldFKInfo['on_delete']!=$consSQL) {
  3182. //this field already has a FK.but needs to be replaced
  3183. continue;
  3184. }
  3185. $oldTable = $table;
  3186. $oldField = $oldFKInfo['from'];
  3187. $oldTargetTable = $oldFKInfo['table'];
  3188. $oldTargetField = $oldFKInfo['to'];
  3189. $restoreFKSQLSnippets .= ", FOREIGN KEY(`$oldField`) REFERENCES `$oldTargetTable`(`$oldTargetField`) ON DELETE ".$oldFKInfo['on_delete'];
  3190. }
  3191. $fkDef = $restoreFKSQLSnippets;
  3192. if ($constraint) {
  3193. $fkDef .= ", FOREIGN KEY(`$field`) REFERENCES `$targetTable`(`$targetField`) ON DELETE CASCADE ";
  3194. }
  3195. else {
  3196. $fkDef .= ", FOREIGN KEY(`$field`) REFERENCES `$targetTable`(`$targetField`) ON DELETE SET NULL ON UPDATE SET NULL";
  3197. }
  3198. $q = array();
  3199. $q[] = "DROP TABLE IF EXISTS tmp_backup;";
  3200. $q[] = "CREATE TEMPORARY TABLE tmp_backup(".implode(',',$oldColumnNames).");";
  3201. $q[] = "INSERT INTO tmp_backup SELECT * FROM `$table`;";
  3202. $q[] = "PRAGMA foreign_keys = 0 ";
  3203. $q[] = "DROP TABLE `$table`;";
  3204. $q[] = "CREATE TABLE `$table` ( `id` INTEGER PRIMARY KEY AUTOINCREMENT $newTableDefStr $fkDef );";
  3205. $q[] = "INSERT INTO `$table` SELECT * FROM tmp_backup;";
  3206. $q[] = "DROP TABLE tmp_backup;";
  3207. $q[] = "PRAGMA foreign_keys = 1 ";
  3208. foreach($q as $sq) {
  3209. $this->adapter->exec($sq);
  3210. }
  3211. return true;
  3212. }
  3213. catch(Exception $e){ return false; }
  3214. }
  3215. /**
  3216. * Add the constraints for a specific database driver: SQLite.
  3217. * @todo Too many arguments; find a way to solve this in a neater way.
  3218. *
  3219. * @param string $table table
  3220. * @param string $table1 table1
  3221. * @param string $table2 table2
  3222. * @param string $property1 property1
  3223. * @param string $property2 property2
  3224. *
  3225. * @return boolean $succes whether the constraint has been applied
  3226. */
  3227. protected function constrain($table, $table1, $table2, $property1, $property2) {
  3228. $writer = $this;
  3229. $adapter = $this->adapter;
  3230. $firstState = $this->buildFK($table,$table1,$property1,'id',true);
  3231. $secondState = $this->buildFK($table,$table2,$property2,'id',true);
  3232. return ($firstState && $secondState);
  3233. }
  3234. /**
  3235. * Removes all tables and views from the database.
  3236. *
  3237. * @return void
  3238. */
  3239. public function wipeAll() {
  3240. $this->adapter->exec('PRAGMA foreign_keys = 0 ');
  3241. foreach($this->getTables() as $t) {
  3242. try{
  3243. $this->adapter->exec("drop table if exists`$t`");
  3244. }
  3245. catch(Exception $e){}
  3246. try{
  3247. $this->adapter->exec("drop view if exists`$t`");
  3248. }
  3249. catch(Exception $e){}
  3250. }
  3251. $this->adapter->exec('PRAGMA foreign_keys = 1 ');
  3252. }
  3253. }
  3254. /**
  3255. * RedBean PostgreSQL Query Writer
  3256. *
  3257. * @file RedBean/QueryWriter/PostgreSQL.php
  3258. * @description QueryWriter for the PostgreSQL database system.
  3259. * @author Gabor de Mooij and the RedBeanPHP Community
  3260. * @license BSD/GPLv2
  3261. *
  3262. * (c) copyright G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community.
  3263. * This source file is subject to the BSD/GPLv2 License that is bundled
  3264. * with this source code in the file license.txt.
  3265. */
  3266. class RedBean_QueryWriter_PostgreSQL extends RedBean_QueryWriter_AQueryWriter implements RedBean_QueryWriter {
  3267. /**
  3268. * DATA TYPE
  3269. * Integer Data Type
  3270. * @var integer
  3271. */
  3272. const C_DATATYPE_INTEGER = 0;
  3273. /**
  3274. * DATA TYPE
  3275. * Double Precision Type
  3276. * @var integer
  3277. */
  3278. const C_DATATYPE_DOUBLE = 1;
  3279. /**
  3280. * DATA TYPE
  3281. * String Data Type
  3282. * @var integer
  3283. */
  3284. const C_DATATYPE_TEXT = 3;
  3285. /**
  3286. * Special type date for storing date values: YYYY-MM-DD
  3287. * @var integer
  3288. */
  3289. const C_DATATYPE_SPECIAL_DATE = 80;
  3290. /**
  3291. * Special type date for storing date values: YYYY-MM-DD HH:MM:SS
  3292. * @var integer
  3293. */
  3294. const C_DATATYPE_SPECIAL_DATETIME = 81;
  3295. const C_DATATYPE_SPECIAL_POINT = 101;
  3296. const C_DATATYPE_SPECIAL_LINE = 102;
  3297. const C_DATATYPE_SPECIAL_LSEG = 103;
  3298. const C_DATATYPE_SPECIAL_BOX = 104;
  3299. const C_DATATYPE_SPECIAL_CIRCLE = 105;
  3300. const C_DATATYPE_SPECIAL_POLYGON = 106;
  3301. /**
  3302. * Specified field type cannot be overruled
  3303. * @var integer
  3304. */
  3305. const C_DATATYPE_SPECIFIED = 99;
  3306. /**
  3307. * Holds Database Adapter
  3308. * @var RedBean_DBAdapter
  3309. */
  3310. protected $adapter;
  3311. /**
  3312. * character to escape keyword table/column names
  3313. * @var string
  3314. */
  3315. protected $quoteCharacter = '"';
  3316. /**
  3317. * Default Value
  3318. * @var string
  3319. */
  3320. protected $defaultValue = 'DEFAULT';
  3321. /**
  3322. * This method returns the datatype to be used for primary key IDS and
  3323. * foreign keys. Returns one if the data type constants.
  3324. *
  3325. * @return integer $const data type to be used for IDS.
  3326. */
  3327. public function getTypeForID() {
  3328. return self::C_DATATYPE_INTEGER;
  3329. }
  3330. /**
  3331. * Returns the insert suffix SQL Snippet
  3332. *
  3333. * @param string $table table
  3334. *
  3335. * @return string $sql SQL Snippet
  3336. */
  3337. protected function getInsertSuffix($table) {
  3338. return 'RETURNING id ';
  3339. }
  3340. /**
  3341. * Constructor
  3342. * The Query Writer Constructor also sets up the database
  3343. *
  3344. * @param RedBean_DBAdapter $adapter adapter
  3345. */
  3346. public function __construct( RedBean_Adapter_DBAdapter $adapter ) {
  3347. $this->typeno_sqltype = array(
  3348. self::C_DATATYPE_INTEGER=>' integer ',
  3349. self::C_DATATYPE_DOUBLE=>' double precision ',
  3350. self::C_DATATYPE_TEXT=>' text ',
  3351. self::C_DATATYPE_SPECIAL_DATE => ' date ',
  3352. self::C_DATATYPE_SPECIAL_DATETIME => ' timestamp without time zone ',
  3353. self::C_DATATYPE_SPECIAL_POINT => ' point ',
  3354. self::C_DATATYPE_SPECIAL_LINE => ' line ',
  3355. self::C_DATATYPE_SPECIAL_LSEG => ' lseg ',
  3356. self::C_DATATYPE_SPECIAL_BOX => ' box ',
  3357. self::C_DATATYPE_SPECIAL_CIRCLE => ' circle ',
  3358. self::C_DATATYPE_SPECIAL_POLYGON => ' polygon ',
  3359. );
  3360. $this->sqltype_typeno = array();
  3361. foreach($this->typeno_sqltype as $k=>$v)
  3362. $this->sqltype_typeno[trim(strtolower($v))]=$k;
  3363. $this->adapter = $adapter;
  3364. parent::__construct();
  3365. }
  3366. /**
  3367. * Returns all tables in the database
  3368. *
  3369. * @return array $tables tables
  3370. */
  3371. public function getTables() {
  3372. return $this->adapter->getCol( "select table_name from information_schema.tables
  3373. where table_schema = 'public'" );
  3374. }
  3375. /**
  3376. * Creates an empty, column-less table for a bean.
  3377. *
  3378. * @param string $table table to create
  3379. */
  3380. public function createTable( $table ) {
  3381. $table = $this->safeTable($table);
  3382. $sql = " CREATE TABLE $table (id SERIAL PRIMARY KEY); ";
  3383. $this->adapter->exec( $sql );
  3384. }
  3385. /**
  3386. * Returns an array containing the column names of the specified table.
  3387. *
  3388. * @param string $table table to get columns from
  3389. *
  3390. * @return array $columns array filled with column (name=>type)
  3391. */
  3392. public function getColumns( $table ) {
  3393. $table = $this->safeTable($table, true);
  3394. $columnsRaw = $this->adapter->get("select column_name, data_type from information_schema.columns where table_name='$table'");
  3395. foreach($columnsRaw as $r) {
  3396. $columns[$r['column_name']]=$r['data_type'];
  3397. }
  3398. return $columns;
  3399. }
  3400. /**
  3401. * Returns the pgSQL Column Type Code (integer) that corresponds
  3402. * to the given value type.
  3403. *
  3404. * @param string $value value to determine type of
  3405. *
  3406. * @return integer $type type code for this value
  3407. */
  3408. public function scanType( $value, $flagSpecial=false ) {
  3409. $this->svalue=$value;
  3410. if ($flagSpecial && $value) {
  3411. if (preg_match('/^\d{4}\-\d\d-\d\d$/',$value)) {
  3412. return RedBean_QueryWriter_PostgreSQL::C_DATATYPE_SPECIAL_DATE;
  3413. }
  3414. if (preg_match('/^\d{4}\-\d\d-\d\d\s\d\d:\d\d:\d\d(\.\d{1,6})?$/',$value)) {
  3415. return RedBean_QueryWriter_PostgreSQL::C_DATATYPE_SPECIAL_DATETIME;
  3416. }
  3417. if (strpos($value,'POINT(')===0) {
  3418. $this->svalue = str_replace('POINT','',$value);
  3419. return RedBean_QueryWriter_PostgreSQL::C_DATATYPE_SPECIAL_POINT;
  3420. }
  3421. if (strpos($value,'LSEG(')===0) {
  3422. $this->svalue = str_replace('LSEG','',$value);
  3423. return RedBean_QueryWriter_PostgreSQL::C_DATATYPE_SPECIAL_LSEG;
  3424. }
  3425. if (strpos($value,'BOX(')===0) {
  3426. $this->svalue = str_replace('BOX','',$value);
  3427. return RedBean_QueryWriter_PostgreSQL::C_DATATYPE_SPECIAL_BOX;
  3428. }
  3429. if (strpos($value,'CIRCLE(')===0) {
  3430. $this->svalue = str_replace('CIRCLE','',$value);
  3431. return RedBean_QueryWriter_PostgreSQL::C_DATATYPE_SPECIAL_CIRCLE;
  3432. }
  3433. if (strpos($value,'POLYGON(')===0) {
  3434. $this->svalue = str_replace('POLYGON','',$value);
  3435. return RedBean_QueryWriter_PostgreSQL::C_DATATYPE_SPECIAL_POLYGON;
  3436. }
  3437. }
  3438. $sz = ($this->startsWithZeros($value));
  3439. if ($sz) return self::C_DATATYPE_TEXT;
  3440. if ($value===null || ($value instanceof RedBean_Driver_PDO_NULL) ||(is_numeric($value)
  3441. && floor($value)==$value
  3442. && $value < 2147483648
  3443. && $value > -2147483648)) {
  3444. return self::C_DATATYPE_INTEGER;
  3445. }
  3446. elseif(is_numeric($value)) {
  3447. return self::C_DATATYPE_DOUBLE;
  3448. }
  3449. else {
  3450. return self::C_DATATYPE_TEXT;
  3451. }
  3452. }
  3453. /**
  3454. * Returns the Type Code for a Column Description.
  3455. * Given an SQL column description this method will return the corresponding
  3456. * code for the writer. If the include specials flag is set it will also
  3457. * return codes for special columns. Otherwise special columns will be identified
  3458. * as specified columns.
  3459. *
  3460. * @param string $typedescription description
  3461. * @param boolean $includeSpecials whether you want to get codes for special columns as well
  3462. *
  3463. * @return integer $typecode code
  3464. */
  3465. public function code( $typedescription, $includeSpecials = false ) {
  3466. $r = ((isset($this->sqltype_typeno[$typedescription])) ? $this->sqltype_typeno[$typedescription] : 99);
  3467. if ($includeSpecials) return $r;
  3468. if ($r > self::C_DATATYPE_SPECIFIED) return self::C_DATATYPE_SPECIFIED;
  3469. return $r;
  3470. }
  3471. /**
  3472. * This method upgrades the column to the specified data type.
  3473. * This methods accepts a type and infers the corresponding table name.
  3474. *
  3475. * @param string $type type / table that needs to be adjusted
  3476. * @param string $column column that needs to be altered
  3477. * @param integer $datatype target data type
  3478. *
  3479. * @return void
  3480. */
  3481. public function widenColumn( $type, $column, $datatype ) {
  3482. $table = $type;
  3483. $type = $datatype;
  3484. $table = $this->safeTable($table);
  3485. $column = $this->safeColumn($column);
  3486. $newtype = $this->typeno_sqltype[$type];
  3487. $changecolumnSQL = "ALTER TABLE $table \n\t ALTER COLUMN $column TYPE $newtype ";
  3488. $this->adapter->exec( $changecolumnSQL );
  3489. }
  3490. /**
  3491. * Adds a Unique index constrain to the table.
  3492. *
  3493. * @param string $table table to add index to
  3494. * @param string $col1 column to be part of index
  3495. * @param string $col2 column 2 to be part of index
  3496. *
  3497. * @return void
  3498. */
  3499. public function addUniqueIndex( $table,$columns ) {
  3500. $table = $this->safeTable($table, true);
  3501. sort($columns); //else we get multiple indexes due to order-effects
  3502. foreach($columns as $k=>$v) {
  3503. $columns[$k]=$this->safeColumn($v);
  3504. }
  3505. $r = $this->adapter->get("SELECT
  3506. i.relname as index_name
  3507. FROM
  3508. pg_class t,
  3509. pg_class i,
  3510. pg_index ix,
  3511. pg_attribute a
  3512. WHERE
  3513. t.oid = ix.indrelid
  3514. AND i.oid = ix.indexrelid
  3515. AND a.attrelid = t.oid
  3516. AND a.attnum = ANY(ix.indkey)
  3517. AND t.relkind = 'r'
  3518. AND t.relname = '$table'
  3519. ORDER BY t.relname, i.relname;");
  3520. $name = "UQ_".sha1($table.implode(',',$columns));
  3521. if ($r) {
  3522. foreach($r as $i) {
  3523. if (strtolower( $i['index_name'] )== strtolower( $name )) {
  3524. return;
  3525. }
  3526. }
  3527. }
  3528. $sql = "ALTER TABLE \"$table\"
  3529. ADD CONSTRAINT $name UNIQUE (".implode(',',$columns).")";
  3530. $this->adapter->exec($sql);
  3531. }
  3532. /**
  3533. * Given an Database Specific SQLState and a list of QueryWriter
  3534. * Standard SQL States this function converts the raw SQL state to a
  3535. * database agnostic ANSI-92 SQL states and checks if the given state
  3536. * is in the list of agnostic states.
  3537. *
  3538. * @param string $state state
  3539. * @param array $list list of states
  3540. *
  3541. * @return boolean $isInArray whether state is in list
  3542. */
  3543. public function sqlStateIn($state, $list) {
  3544. $stateMap = array(
  3545. '42P01'=>RedBean_QueryWriter::C_SQLSTATE_NO_SUCH_TABLE,
  3546. '42703'=>RedBean_QueryWriter::C_SQLSTATE_NO_SUCH_COLUMN,
  3547. '23505'=>RedBean_QueryWriter::C_SQLSTATE_INTEGRITY_CONSTRAINT_VIOLATION
  3548. );
  3549. return in_array((isset($stateMap[$state]) ? $stateMap[$state] : '0'),$list);
  3550. }
  3551. /**
  3552. * Adds a foreign key to a table. The foreign key will not have any action; you
  3553. * may configure this afterwards.
  3554. *
  3555. * @param string $type type you want to modify table of
  3556. * @param string $targetType target type
  3557. * @param string $field field of the type that needs to get the fk
  3558. * @param string $targetField field where the fk needs to point to
  3559. *
  3560. * @return bool $success whether an FK has been added
  3561. */
  3562. public function addFK( $type, $targetType, $field, $targetField, $isDep = false) {
  3563. try{
  3564. $table = $this->safeTable($type);
  3565. $column = $this->safeColumn($field);
  3566. $tableNoQ = $this->safeTable($type,true);
  3567. $columnNoQ = $this->safeColumn($field,true);
  3568. $targetTable = $this->safeTable($targetType);
  3569. $targetTableNoQ = $this->safeTable($targetType,true);
  3570. $targetColumn = $this->safeColumn($targetField);
  3571. $targetColumnNoQ = $this->safeColumn($targetField,true);
  3572. $sql = "SELECT
  3573. tc.constraint_name,
  3574. tc.table_name,
  3575. kcu.column_name,
  3576. ccu.table_name AS foreign_table_name,
  3577. ccu.column_name AS foreign_column_name,
  3578. rc.delete_rule
  3579. FROM
  3580. information_schema.table_constraints AS tc
  3581. JOIN information_schema.key_column_usage AS kcu ON tc.constraint_name = kcu.constraint_name
  3582. JOIN information_schema.constraint_column_usage AS ccu ON ccu.constraint_name = tc.constraint_name
  3583. JOIN information_schema.referential_constraints AS rc ON ccu.constraint_name = rc.constraint_name
  3584. WHERE constraint_type = 'FOREIGN KEY' AND tc.table_catalog=current_database()
  3585. AND tc.table_name = '$tableNoQ'
  3586. AND ccu.table_name = '$targetTableNoQ'
  3587. AND kcu.column_name = '$columnNoQ'
  3588. AND ccu.column_name = '$targetColumnNoQ'
  3589. ";
  3590. $row = $this->adapter->getRow($sql);
  3591. $flagAddKey = false;
  3592. if (!$row) $flagAddKey = true;
  3593. if ($row) {
  3594. if (($row['delete_rule']=='SET NULL' && $isDep) ||
  3595. ($row['delete_rule']!='SET NULL' && !$isDep)) {
  3596. //delete old key
  3597. $flagAddKey = true; //and order a new one
  3598. $cName = $row['constraint_name'];
  3599. $sql = "ALTER TABLE $table DROP CONSTRAINT $cName ";
  3600. $this->adapter->exec($sql);
  3601. }
  3602. }
  3603. if ($flagAddKey) {
  3604. $delRule = ($isDep ? 'CASCADE' : 'SET NULL');
  3605. $this->adapter->exec("ALTER TABLE $table
  3606. ADD FOREIGN KEY ( $column ) REFERENCES $targetTable (
  3607. $targetColumn) ON DELETE $delRule ON UPDATE SET NULL DEFERRABLE ;");
  3608. return true;
  3609. }
  3610. return false;
  3611. }
  3612. catch(Exception $e){ return false; }
  3613. }
  3614. /**
  3615. * Add the constraints for a specific database driver: PostgreSQL.
  3616. * @todo Too many arguments; find a way to solve this in a neater way.
  3617. *
  3618. * @param string $table table
  3619. * @param string $table1 table1
  3620. * @param string $table2 table2
  3621. * @param string $property1 property1
  3622. * @param string $property2 property2
  3623. *
  3624. * @return boolean $succes whether the constraint has been applied
  3625. */
  3626. protected function constrain($table, $table1, $table2, $property1, $property2) {
  3627. try{
  3628. $writer = $this;
  3629. $adapter = $this->adapter;
  3630. $fkCode = 'fk'.md5($table.$property1.$property2);
  3631. $sql = "
  3632. SELECT
  3633. c.oid,
  3634. n.nspname,
  3635. c.relname,
  3636. n2.nspname,
  3637. c2.relname,
  3638. cons.conname
  3639. FROM pg_class c
  3640. JOIN pg_namespace n ON n.oid = c.relnamespace
  3641. LEFT OUTER JOIN pg_constraint cons ON cons.conrelid = c.oid
  3642. LEFT OUTER JOIN pg_class c2 ON cons.confrelid = c2.oid
  3643. LEFT OUTER JOIN pg_namespace n2 ON n2.oid = c2.relnamespace
  3644. WHERE c.relkind = 'r'
  3645. AND n.nspname IN ('public')
  3646. AND (cons.contype = 'f' OR cons.contype IS NULL)
  3647. AND
  3648. ( cons.conname = '{$fkCode}a' OR cons.conname = '{$fkCode}b' )
  3649. ";
  3650. $rows = $adapter->get( $sql );
  3651. if (!count($rows)) {
  3652. $sql1 = "ALTER TABLE \"$table\" ADD CONSTRAINT
  3653. {$fkCode}a FOREIGN KEY ($property1)
  3654. REFERENCES \"$table1\" (id) ON DELETE CASCADE ";
  3655. $sql2 = "ALTER TABLE \"$table\" ADD CONSTRAINT
  3656. {$fkCode}b FOREIGN KEY ($property2)
  3657. REFERENCES \"$table2\" (id) ON DELETE CASCADE ";
  3658. $adapter->exec($sql1);
  3659. $adapter->exec($sql2);
  3660. }
  3661. return true;
  3662. }
  3663. catch(Exception $e){ return false; }
  3664. }
  3665. /**
  3666. * Removes all tables and views from the database.
  3667. */
  3668. public function wipeAll() {
  3669. $this->adapter->exec('SET CONSTRAINTS ALL DEFERRED');
  3670. foreach($this->getTables() as $t) {
  3671. $t = $this->noKW($t);
  3672. try{
  3673. $this->adapter->exec("drop table if exists $t CASCADE ");
  3674. }
  3675. catch(Exception $e){ }
  3676. try{
  3677. $this->adapter->exec("drop view if exists $t CASCADE ");
  3678. }
  3679. catch(Exception $e){ throw $e; }
  3680. }
  3681. $this->adapter->exec('SET CONSTRAINTS ALL IMMEDIATE');
  3682. }
  3683. }
  3684. /**
  3685. * RedBean Exception Base
  3686. *
  3687. * @file RedBean/Exception.php
  3688. * @description Represents the base class
  3689. * for RedBean Exceptions
  3690. * @author Gabor de Mooij and the RedBeanPHP Community
  3691. * @license BSD/GPLv2
  3692. *
  3693. *
  3694. * copyright (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community
  3695. * This source file is subject to the BSD/GPLv2 License that is bundled
  3696. * with this source code in the file license.txt.
  3697. */
  3698. class RedBean_Exception extends Exception {}
  3699. /**
  3700. * RedBean Exception SQL
  3701. *
  3702. * @file RedBean/Exception/SQL.php
  3703. * @description Represents a generic database exception independent of the
  3704. * underlying driver.
  3705. * @author Gabor de Mooij and the RedBeanPHP Community
  3706. * @license BSD/GPLv2
  3707. *
  3708. *
  3709. * (c) copyright G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community.
  3710. * This source file is subject to the BSD/GPLv2 License that is bundled
  3711. * with this source code in the file license.txt.
  3712. */
  3713. class RedBean_Exception_SQL extends Exception {
  3714. /**
  3715. * Holds the current SQL Strate code.
  3716. * @var string
  3717. */
  3718. private $sqlState;
  3719. /**
  3720. * Returns an ANSI-92 compliant SQL state.
  3721. *
  3722. * @return string $state ANSI state code
  3723. */
  3724. public function getSQLState() {
  3725. return $this->sqlState;
  3726. }
  3727. /**
  3728. * @todo parse state to verify valid ANSI92!
  3729. * Stores ANSI-92 compliant SQL state.
  3730. *
  3731. * @param string $sqlState code
  3732. *
  3733. * @return void
  3734. */
  3735. public function setSQLState( $sqlState ) {
  3736. $this->sqlState = $sqlState;
  3737. }
  3738. /**
  3739. * To String prints both code and SQL state.
  3740. *
  3741. * @return string $message prints this exception instance as a string
  3742. */
  3743. public function __toString() {
  3744. return '['.$this->getSQLState().'] - '.$this->getMessage();
  3745. }
  3746. }
  3747. /**
  3748. * Exception Security.
  3749. * Part of the RedBean Exceptions Mechanism.
  3750. *
  3751. * @file RedBean/Exception
  3752. * @description Represents a subtype in the RedBean Exception System.
  3753. * @author Gabor de Mooij and the RedBeanPHP Community
  3754. * @license BSD/GPLv2
  3755. *
  3756. *
  3757. * (c) copyright G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community.
  3758. * This source file is subject to the BSD/GPLv2 License that is bundled
  3759. * with this source code in the file license.txt.
  3760. */
  3761. class RedBean_Exception_Security extends RedBean_Exception {}
  3762. /**
  3763. * RedBean Object Oriented DataBase
  3764. *
  3765. * @file RedBean/OODB.php
  3766. * @description RedBean OODB
  3767. *
  3768. * @author Gabor de Mooij and the RedBeanPHP community
  3769. * @license BSD/GPLv2
  3770. *
  3771. * The RedBean OODB Class is the main class of RedBeanPHP.
  3772. * It takes RedBean_OODBBean objects and stores them to and loads them from the
  3773. * database as well as providing other CRUD functions. This class acts as a
  3774. * object database.
  3775. *
  3776. * copyright (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community
  3777. * This source file is subject to the BSD/GPLv2 License that is bundled
  3778. * with this source code in the file license.txt.
  3779. */
  3780. class RedBean_OODB extends RedBean_Observable {
  3781. /**
  3782. * Chill mode, for fluid mode but with a list of beans / types that
  3783. * are considered to be stable and don't need to be modified.
  3784. * @var array
  3785. */
  3786. private $chillList = array();
  3787. /**
  3788. * List of dependencies. Format: $type => array($depensOnMe, $andMe)
  3789. * @var array
  3790. */
  3791. private $dep = array();
  3792. /**
  3793. * Secret stash. Used for batch loading.
  3794. * @var array
  3795. */
  3796. private $stash = NULL;
  3797. /**
  3798. * Contains the writer for OODB.
  3799. * @var RedBean_Adapter_DBAdapter
  3800. */
  3801. private $writer;
  3802. /**
  3803. * Whether this instance of OODB is frozen or not.
  3804. * In frozen mode the schema will not de modified, in fluid mode
  3805. * the schema can be adjusted to meet the needs of the developer.
  3806. * @var boolean
  3807. */
  3808. private $isFrozen = false;
  3809. /**
  3810. * Bean Helper. The bean helper to give to the beans. Bean Helpers
  3811. * assist beans in getting hold of a toolbox.
  3812. * @var null|\RedBean_BeanHelperFacade
  3813. */
  3814. private $beanhelper = null;
  3815. /**
  3816. * The RedBean OODB Class is the main class of RedBean.
  3817. * It takes RedBean_OODBBean objects and stores them to and loads them from the
  3818. * database as well as providing other CRUD functions. This class acts as a
  3819. * object database.
  3820. * Constructor, requires a DBAadapter (dependency inversion)
  3821. * @param RedBean_Adapter_DBAdapter $adapter
  3822. */
  3823. public function __construct( $writer ) {
  3824. if ($writer instanceof RedBean_QueryWriter) {
  3825. $this->writer = $writer;
  3826. }
  3827. $this->beanhelper = new RedBean_BeanHelperFacade();
  3828. }
  3829. /**
  3830. * Toggles fluid or frozen mode. In fluid mode the database
  3831. * structure is adjusted to accomodate your objects. In frozen mode
  3832. * this is not the case.
  3833. *
  3834. * You can also pass an array containing a selection of frozen types.
  3835. * Let's call this chilly mode, it's just like fluid mode except that
  3836. * certain types (i.e. tables) aren't touched.
  3837. *
  3838. * @param boolean|array $trueFalse
  3839. */
  3840. public function freeze( $tf ) {
  3841. if (is_array($tf)) {
  3842. $this->chillList = $tf;
  3843. $this->isFrozen = false;
  3844. }
  3845. else
  3846. $this->isFrozen = (boolean) $tf;
  3847. }
  3848. /**
  3849. * Returns the current mode of operation of RedBean.
  3850. * In fluid mode the database
  3851. * structure is adjusted to accomodate your objects.
  3852. * In frozen mode
  3853. * this is not the case.
  3854. *
  3855. * @return boolean $yesNo TRUE if frozen, FALSE otherwise
  3856. */
  3857. public function isFrozen() {
  3858. return (bool) $this->isFrozen;
  3859. }
  3860. /**
  3861. * Dispenses a new bean (a RedBean_OODBBean Bean Object)
  3862. * of the specified type. Always
  3863. * use this function to get an empty bean object. Never
  3864. * instantiate a RedBean_OODBBean yourself because it needs
  3865. * to be configured before you can use it with RedBean. This
  3866. * function applies the appropriate initialization /
  3867. * configuration for you.
  3868. *
  3869. * @param string $type type of bean you want to dispense
  3870. *
  3871. * @return RedBean_OODBBean $bean the new bean instance
  3872. */
  3873. public function dispense($type ) {
  3874. $this->signal( 'before_dispense', $type );
  3875. $bean = new RedBean_OODBBean();
  3876. $bean->setBeanHelper($this->beanhelper);
  3877. $bean->setMeta('type',$type );
  3878. $bean->setMeta('sys.id','id');
  3879. $bean->id = 0;
  3880. if (!$this->isFrozen) $this->check( $bean );
  3881. $bean->setMeta('tainted',true);
  3882. $this->signal('dispense',$bean );
  3883. return $bean;
  3884. }
  3885. /**
  3886. * Sets bean helper to be given to beans.
  3887. * Bean helpers assist beans in getting a reference to a toolbox.
  3888. *
  3889. * @param RedBean_IBeanHelper $beanhelper helper
  3890. *
  3891. * @return void
  3892. */
  3893. public function setBeanHelper( RedBean_IBeanHelper $beanhelper) {
  3894. $this->beanhelper = $beanhelper;
  3895. }
  3896. /**
  3897. * Checks whether a RedBean_OODBBean bean is valid.
  3898. * If the type is not valid or the ID is not valid it will
  3899. * throw an exception: RedBean_Exception_Security.
  3900. * @throws RedBean_Exception_Security $exception
  3901. *
  3902. * @param RedBean_OODBBean $bean the bean that needs to be checked
  3903. *
  3904. * @return void
  3905. */
  3906. public function check( RedBean_OODBBean $bean ) {
  3907. //Is all meta information present?
  3908. if (!isset($bean->id) ) {
  3909. throw new RedBean_Exception_Security('Bean has incomplete Meta Information id ');
  3910. }
  3911. if (!($bean->getMeta('type'))) {
  3912. throw new RedBean_Exception_Security('Bean has incomplete Meta Information II');
  3913. }
  3914. //Pattern of allowed characters
  3915. $pattern = '/[^a-z0-9_]/i';
  3916. //Does the type contain invalid characters?
  3917. if (preg_match($pattern,$bean->getMeta('type'))) {
  3918. throw new RedBean_Exception_Security('Bean Type is invalid');
  3919. }
  3920. //Are the properties and values valid?
  3921. foreach($bean as $prop=>$value) {
  3922. if (
  3923. is_array($value) ||
  3924. (is_object($value)) ||
  3925. strlen($prop)<1 ||
  3926. preg_match($pattern,$prop)
  3927. ) {
  3928. throw new RedBean_Exception_Security("Invalid Bean: property $prop ");
  3929. }
  3930. }
  3931. }
  3932. /**
  3933. * Searches the database for a bean that matches conditions $conditions and sql $addSQL
  3934. * and returns an array containing all the beans that have been found.
  3935. *
  3936. * Conditions need to take form:
  3937. *
  3938. * array(
  3939. * 'PROPERTY' => array( POSSIBLE VALUES... 'John','Steve' )
  3940. * 'PROPERTY' => array( POSSIBLE VALUES... )
  3941. * );
  3942. *
  3943. * All conditions are glued together using the AND-operator, while all value lists
  3944. * are glued using IN-operators thus acting as OR-conditions.
  3945. *
  3946. * Note that you can use property names; the columns will be extracted using the
  3947. * appropriate bean formatter.
  3948. *
  3949. * @throws RedBean_Exception_SQL
  3950. *
  3951. * @param string $type type of beans you are looking for
  3952. * @param array $conditions list of conditions
  3953. * @param string $addSQL SQL to be used in query
  3954. * @param boolean $all whether you prefer to use a WHERE clause or not (TRUE = not)
  3955. *
  3956. * @return array $beans resulting beans
  3957. */
  3958. public function find($type,$conditions=array(),$addSQL=null,$all=false) {
  3959. try {
  3960. $beans = $this->convertToBeans($type,$this->writer->selectRecord($type,$conditions,$addSQL,false,false,$all));
  3961. return $beans;
  3962. }catch(RedBean_Exception_SQL $e) {
  3963. if (!$this->writer->sqlStateIn($e->getSQLState(),
  3964. array(
  3965. RedBean_QueryWriter::C_SQLSTATE_NO_SUCH_TABLE,
  3966. RedBean_QueryWriter::C_SQLSTATE_NO_SUCH_COLUMN)
  3967. )) throw $e;
  3968. }
  3969. return array();
  3970. }
  3971. /**
  3972. * Checks whether the specified table already exists in the database.
  3973. * Not part of the Object Database interface!
  3974. *
  3975. * @param string $table table name (not type!)
  3976. *
  3977. * @return boolean $exists whether the given table exists within this database.
  3978. */
  3979. public function tableExists($table) {
  3980. //does this table exist?
  3981. $tables = $this->writer->getTables();
  3982. return in_array(($table), $tables);
  3983. }
  3984. /**
  3985. * Processes all column based build commands.
  3986. * A build command is an additional instruction for the Query Writer. It is processed only when
  3987. * a column gets created. The build command is often used to instruct the writer to write some
  3988. * extra SQL to create indexes or constraints. Build commands are stored in meta data of the bean.
  3989. * They are only for internal use, try to refrain from using them in your code directly.
  3990. *
  3991. * @param string $table name of the table to process build commands for
  3992. * @param string $property name of the property to process build commands for
  3993. * @param RedBean_OODBBean $bean bean that contains the build commands
  3994. *
  3995. * @return void
  3996. */
  3997. protected function processBuildCommands($table, $property, RedBean_OODBBean $bean) {
  3998. if ($inx = ($bean->getMeta('buildcommand.indexes'))) {
  3999. if (isset($inx[$property])) $this->writer->addIndex($table,$inx[$property],$property);
  4000. }
  4001. }
  4002. /**
  4003. * Process groups. Internal function. Processes different kind of groups for
  4004. * storage function. Given a list of original beans and a list of current beans,
  4005. * this function calculates which beans remain in the list (residue), which
  4006. * have been deleted (are in the trashcan) and which beans have been added
  4007. * (additions).
  4008. *
  4009. * @param array $originals originals
  4010. * @param array $current the current beans
  4011. * @param array $additions beans that have been added
  4012. * @param array $trashcan beans that have been deleted
  4013. * @param array $residue beans that have been left untouched
  4014. *
  4015. * @return array $result new relational state
  4016. */
  4017. private function processGroups( $originals, $current, $additions, $trashcan, $residue ) {
  4018. return array(
  4019. array_merge($additions,array_diff($current,$originals)),
  4020. array_merge($trashcan,array_diff($originals,$current)),
  4021. array_merge($residue,array_intersect($current,$originals))
  4022. );
  4023. }
  4024. /**
  4025. * Figures out the desired type given the cast string ID.
  4026. *
  4027. * @param string $cast cast identifier
  4028. *
  4029. * @return integer $typeno
  4030. */
  4031. private function getTypeFromCast($cast) {
  4032. if ($cast=='string') {
  4033. $typeno = $this->writer->scanType('STRING');
  4034. }
  4035. elseif ($cast=='id') {
  4036. $typeno = $this->writer->getTypeForID();
  4037. }
  4038. elseif(isset($this->writer->sqltype_typeno[$cast])) {
  4039. $typeno = $this->writer->sqltype_typeno[$cast];
  4040. }
  4041. else {
  4042. throw new RedBean_Exception('Invalid Cast');
  4043. }
  4044. return $typeno;
  4045. }
  4046. /**
  4047. * Processes an embedded bean. First the bean gets unboxed if possible.
  4048. * Then, the bean is stored if needed and finally the ID of the bean
  4049. * will be returned.
  4050. *
  4051. * @param RedBean_OODBBean|Model $v the bean or model
  4052. *
  4053. * @return integer $id
  4054. */
  4055. private function prepareEmbeddedBean($v) {
  4056. if (!$v->id || $v->getMeta('tainted')) {
  4057. $this->store($v);
  4058. }
  4059. return $v->id;
  4060. }
  4061. /**
  4062. * Stores a bean in the database. This function takes a
  4063. * RedBean_OODBBean Bean Object $bean and stores it
  4064. * in the database. If the database schema is not compatible
  4065. * with this bean and RedBean runs in fluid mode the schema
  4066. * will be altered to store the bean correctly.
  4067. * If the database schema is not compatible with this bean and
  4068. * RedBean runs in frozen mode it will throw an exception.
  4069. * This function returns the primary key ID of the inserted
  4070. * bean.
  4071. *
  4072. * @throws RedBean_Exception_Security $exception
  4073. *
  4074. * @param RedBean_OODBBean | RedBean_SimpleModel $bean bean to store
  4075. *
  4076. * @return integer $newid resulting ID of the new bean
  4077. */
  4078. public function store( $bean ) {
  4079. if ($bean instanceof RedBean_SimpleModel) $bean = $bean->unbox();
  4080. if (!($bean instanceof RedBean_OODBBean)) throw new RedBean_Exception_Security('OODB Store requires a bean, got: '.gettype($bean));
  4081. $processLists = false;
  4082. foreach($bean as $k=>$v) if (is_array($v) || is_object($v)) { $processLists = true; break; }
  4083. if (!$processLists && !$bean->getMeta('tainted')) return $bean->getID();
  4084. $this->signal('update', $bean );
  4085. foreach($bean as $k=>$v) if (is_array($v) || is_object($v)) { $processLists = true; break; }
  4086. if ($processLists) {
  4087. //Define groups
  4088. $sharedAdditions = $sharedTrashcan = $sharedresidue = $sharedItems = array();
  4089. $ownAdditions = $ownTrashcan = $ownresidue = array();
  4090. $tmpCollectionStore = array();
  4091. $embeddedBeans = array();
  4092. foreach($bean as $p=>$v) {
  4093. if ($v instanceof RedBean_SimpleModel) $v = $v->unbox();
  4094. if ($v instanceof RedBean_OODBBean) {
  4095. $linkField = $p.'_id';
  4096. $bean->$linkField = $this->prepareEmbeddedBean($v);
  4097. $bean->setMeta('cast.'.$linkField,'id');
  4098. $embeddedBeans[$linkField] = $v;
  4099. $tmpCollectionStore[$p]=$bean->$p;
  4100. $bean->removeProperty($p);
  4101. }
  4102. if (is_array($v)) {
  4103. $originals = $bean->getMeta('sys.shadow.'.$p);
  4104. if (!$originals) $originals = array();
  4105. if (strpos($p,'own')===0) {
  4106. list($ownAdditions,$ownTrashcan,$ownresidue)=$this->processGroups($originals,$v,$ownAdditions,$ownTrashcan,$ownresidue);
  4107. $bean->removeProperty($p);
  4108. }
  4109. elseif (strpos($p,'shared')===0) {
  4110. list($sharedAdditions,$sharedTrashcan,$sharedresidue)=$this->processGroups($originals,$v,$sharedAdditions,$sharedTrashcan,$sharedresidue);
  4111. $bean->removeProperty($p);
  4112. }
  4113. else {}
  4114. }
  4115. }
  4116. }
  4117. $this->storeBean($bean);
  4118. if ($processLists) {
  4119. $this->processEmbeddedBeans($bean,$embeddedBeans);
  4120. $myFieldLink = $bean->getMeta('type').'_id';
  4121. //Handle related beans
  4122. $this->processTrashcan($bean,$ownTrashcan);
  4123. $this->processAdditions($bean,$ownAdditions);
  4124. $this->processResidue($ownresidue);
  4125. foreach($sharedTrashcan as $trash) $this->assocManager->unassociate($trash,$bean);
  4126. $this->processSharedAdditions($bean,$sharedAdditions);
  4127. foreach($sharedresidue as $residue) $this->store($residue);
  4128. }
  4129. $this->signal('after_update',$bean);
  4130. return (int) $bean->id;
  4131. }
  4132. /**
  4133. * Stores a cleaned bean; i.e. only scalar values. This is the core of the store()
  4134. * method. When all lists and embedded beans (parent objects) have been processed and
  4135. * removed from the original bean the bean is passed to this method to be stored
  4136. * in the database.
  4137. *
  4138. * @param RedBean_OODBBean $bean the clean bean
  4139. */
  4140. private function storeBean(RedBean_OODBBean $bean) {
  4141. if (!$this->isFrozen) $this->check($bean);
  4142. //what table does it want
  4143. $table = $bean->getMeta('type');
  4144. if ($bean->getMeta('tainted')) {
  4145. //Does table exist? If not, create
  4146. if (!$this->isFrozen && !$this->tableExists($this->writer->safeTable($table,true))) {
  4147. $this->writer->createTable( $table );
  4148. $bean->setMeta('buildreport.flags.created',true);
  4149. }
  4150. if (!$this->isFrozen) {
  4151. $columns = $this->writer->getColumns($table) ;
  4152. }
  4153. //does the table fit?
  4154. $insertvalues = array();
  4155. $insertcolumns = array();
  4156. $updatevalues = array();
  4157. foreach( $bean as $p=>$v ) {
  4158. $origV = $v;
  4159. if ($p!='id') {
  4160. if (!$this->isFrozen) {
  4161. //Not in the chill list?
  4162. if (!in_array($bean->getMeta('type'),$this->chillList)) {
  4163. //Does the user want to specify the type?
  4164. if ($bean->getMeta("cast.$p",-1)!==-1) {
  4165. $cast = $bean->getMeta("cast.$p");
  4166. $typeno = $this->getTypeFromCast($cast);
  4167. }
  4168. else {
  4169. $cast = false;
  4170. //What kind of property are we dealing with?
  4171. $typeno = $this->writer->scanType($v,true);
  4172. $v = $this->writer->getValue();
  4173. }
  4174. //Is this property represented in the table?
  4175. if (isset($columns[$this->writer->safeColumn($p,true)])) {
  4176. //rescan
  4177. $v = $origV;
  4178. if (!$cast) $typeno = $this->writer->scanType($v,false);
  4179. //yes it is, does it still fit?
  4180. $sqlt = $this->writer->code($columns[$this->writer->safeColumn($p,true)]);
  4181. if ($typeno > $sqlt) {
  4182. //no, we have to widen the database column type
  4183. $this->writer->widenColumn( $table, $p, $typeno );
  4184. $bean->setMeta('buildreport.flags.widen',true);
  4185. }
  4186. }
  4187. else {
  4188. //no it is not
  4189. $this->writer->addColumn($table, $p, $typeno);
  4190. $bean->setMeta('buildreport.flags.addcolumn',true);
  4191. //@todo: move build commands here... more practical
  4192. $this->processBuildCommands($table,$p,$bean);
  4193. }
  4194. }
  4195. }
  4196. //Okay, now we are sure that the property value will fit
  4197. $insertvalues[] = $v;
  4198. $insertcolumns[] = $p;
  4199. $updatevalues[] = array( 'property'=>$p, 'value'=>$v );
  4200. }
  4201. }
  4202. if (!$this->isFrozen && ($uniques = $bean->getMeta('buildcommand.unique'))) {
  4203. foreach($uniques as $unique) $this->writer->addUniqueIndex( $table, $unique );
  4204. }
  4205. $rs = $this->writer->updateRecord( $table, $updatevalues, $bean->id );
  4206. $bean->id = $rs;
  4207. $bean->setMeta('tainted',false);
  4208. }
  4209. }
  4210. /**
  4211. * Processes a list of beans from a bean. A bean may contain lists. This
  4212. * method handles shared addition lists; i.e. the $bean->sharedObject properties.
  4213. *
  4214. * @param RedBean_OODBBean $bean the bean
  4215. * @param array $sharedAdditions list with shared additions
  4216. */
  4217. private function processSharedAdditions($bean,$sharedAdditions) {
  4218. foreach($sharedAdditions as $addition) {
  4219. if ($addition instanceof RedBean_OODBBean) {
  4220. $this->assocManager->associate($addition,$bean);
  4221. }
  4222. else {
  4223. throw new RedBean_Exception_Security('Array may only contain RedBean_OODBBeans');
  4224. }
  4225. }
  4226. }
  4227. /**
  4228. * Processes a list of beans from a bean. A bean may contain lists. This
  4229. * method handles own lists; i.e. the $bean->ownObject properties.
  4230. * A residue is a bean in an own-list that stays where it is. This method
  4231. * checks if there have been any modification to this bean, in that case
  4232. * the bean is stored once again, otherwise the bean will be left untouched.
  4233. *
  4234. * @param RedBean_OODBBean $bean the bean
  4235. * @param array $ownresidue list
  4236. */
  4237. private function processResidue($ownresidue) {
  4238. foreach($ownresidue as $residue) {
  4239. if ($residue->getMeta('tainted')) {
  4240. $this->store($residue);
  4241. }
  4242. }
  4243. }
  4244. /**
  4245. * Processes a list of beans from a bean. A bean may contain lists. This
  4246. * method handles own lists; i.e. the $bean->ownObject properties.
  4247. * A trash can bean is a bean in an own-list that has been removed
  4248. * (when checked with the shadow). This method
  4249. * checks if the bean is also in the dependency list. If it is the bean will be removed.
  4250. * If not, the connection between the bean and the owner bean will be broken by
  4251. * setting the ID to NULL.
  4252. *
  4253. * @param RedBean_OODBBean $bean the bean
  4254. * @param array $ownTrashcan list
  4255. */
  4256. private function processTrashcan($bean,$ownTrashcan) {
  4257. $myFieldLink = $bean->getMeta('type').'_id';
  4258. foreach($ownTrashcan as $trash) {
  4259. if (isset($this->dep[$trash->getMeta('type')]) && in_array($bean->getMeta('type'),$this->dep[$trash->getMeta('type')])) {
  4260. $this->trash($trash);
  4261. }
  4262. else {
  4263. $trash->$myFieldLink = null;
  4264. $this->store($trash);
  4265. }
  4266. }
  4267. }
  4268. /**
  4269. * Processes embedded beans.
  4270. * Each embedded bean will be indexed and foreign keys will
  4271. * be created if the bean is in the dependency list.
  4272. *
  4273. * @param RedBean_OODBBean $bean bean
  4274. * @param array $embeddedBeans embedded beans
  4275. */
  4276. private function processEmbeddedBeans($bean, $embeddedBeans) {
  4277. foreach($embeddedBeans as $linkField=>$embeddedBean) {
  4278. if (!$this->isFrozen) {
  4279. $this->writer->addIndex($bean->getMeta('type'),
  4280. 'index_foreignkey_'.$embeddedBean->getMeta('type'),
  4281. $linkField);
  4282. $isDep = $this->isDependentOn($bean->getMeta('type'),$embeddedBean->getMeta('type'));
  4283. $this->writer->addFK($bean->getMeta('type'),$embeddedBean->getMeta('type'),$linkField,'id',$isDep);
  4284. }
  4285. }
  4286. }
  4287. /**
  4288. * Part of the store() functionality.
  4289. * Handles all new additions after the bean has been saved.
  4290. * Stores addition bean in own-list, extracts the id and
  4291. * adds a foreign key. Also adds a constraint in case the type is
  4292. * in the dependent list.
  4293. *
  4294. * @param RedBean_OODBBean $bean bean
  4295. * @param array $ownAdditions list of addition beans in own-list
  4296. */
  4297. private function processAdditions($bean,$ownAdditions) {
  4298. $myFieldLink = $bean->getMeta('type').'_id';
  4299. foreach($ownAdditions as $addition) {
  4300. if ($addition instanceof RedBean_OODBBean) {
  4301. $addition->$myFieldLink = $bean->id;
  4302. $addition->setMeta('cast.'.$myFieldLink,'id');
  4303. $this->store($addition);
  4304. if (!$this->isFrozen) {
  4305. $this->writer->addIndex($addition->getMeta('type'),
  4306. 'index_foreignkey_'.$bean->getMeta('type'),
  4307. $myFieldLink);
  4308. $isDep = $this->isDependentOn($addition->getMeta('type'),$bean->getMeta('type'));
  4309. $this->writer->addFK($addition->getMeta('type'),$bean->getMeta('type'),$myFieldLink,'id',$isDep);
  4310. }
  4311. }
  4312. else {
  4313. throw new RedBean_Exception_Security('Array may only contain RedBean_OODBBeans');
  4314. }
  4315. }
  4316. }
  4317. /**
  4318. * Checks whether reference type has been marked as dependent on target type.
  4319. * This is the result of setting reference type as a key in R::dependencies() and
  4320. * putting target type in its array.
  4321. *
  4322. * @param string $refType reference type
  4323. * @param string $otherType other type / target type
  4324. *
  4325. * @return boolean
  4326. */
  4327. protected function isDependentOn($refType,$otherType) {
  4328. return (boolean) (isset($this->dep[$refType]) && in_array($otherType,$this->dep[$refType]));
  4329. }
  4330. /**
  4331. * Loads a bean from the object database.
  4332. * It searches for a RedBean_OODBBean Bean Object in the
  4333. * database. It does not matter how this bean has been stored.
  4334. * RedBean uses the primary key ID $id and the string $type
  4335. * to find the bean. The $type specifies what kind of bean you
  4336. * are looking for; this is the same type as used with the
  4337. * dispense() function. If RedBean finds the bean it will return
  4338. * the RedBean_OODB Bean object; if it cannot find the bean
  4339. * RedBean will return a new bean of type $type and with
  4340. * primary key ID 0. In the latter case it acts basically the
  4341. * same as dispense().
  4342. *
  4343. * Important note:
  4344. * If the bean cannot be found in the database a new bean of
  4345. * the specified type will be generated and returned.
  4346. *
  4347. * @param string $type type of bean you want to load
  4348. * @param integer $id ID of the bean you want to load
  4349. *
  4350. * @return RedBean_OODBBean $bean loaded bean
  4351. */
  4352. public function load($type,$id) {
  4353. $this->signal('before_open',array('type'=>$type,'id'=>$id));
  4354. $bean = $this->dispense( $type );
  4355. if ($this->stash && isset($this->stash[$id])) {
  4356. $row = $this->stash[$id];
  4357. }
  4358. else {
  4359. try {
  4360. $rows = $this->writer->selectRecord($type,array('id'=>array($id)));
  4361. }catch(RedBean_Exception_SQL $e ) {
  4362. if (
  4363. $this->writer->sqlStateIn($e->getSQLState(),
  4364. array(
  4365. RedBean_QueryWriter::C_SQLSTATE_NO_SUCH_COLUMN,
  4366. RedBean_QueryWriter::C_SQLSTATE_NO_SUCH_TABLE)
  4367. )
  4368. ) {
  4369. $rows = 0;
  4370. if ($this->isFrozen) throw $e; //only throw if frozen;
  4371. }
  4372. }
  4373. if (!$rows) return $bean; // $this->dispense($type); -- no need...
  4374. $row = array_pop($rows);
  4375. }
  4376. foreach($row as $p=>$v) {
  4377. //populate the bean with the database row
  4378. $bean->$p = $v;
  4379. }
  4380. $this->signal('open',$bean );
  4381. $bean->setMeta('tainted',false);
  4382. return $bean;
  4383. }
  4384. /**
  4385. * Removes a bean from the database.
  4386. * This function will remove the specified RedBean_OODBBean
  4387. * Bean Object from the database.
  4388. *
  4389. * @throws RedBean_Exception_Security $exception
  4390. *
  4391. * @param RedBean_OODBBean|RedBean_SimpleModel $bean bean you want to remove from database
  4392. */
  4393. public function trash( $bean ) {
  4394. if ($bean instanceof RedBean_SimpleModel) $bean = $bean->unbox();
  4395. if (!($bean instanceof RedBean_OODBBean)) throw new RedBean_Exception_Security('OODB Store requires a bean, got: '.gettype($bean));
  4396. $this->signal('delete',$bean);
  4397. foreach($bean as $p=>$v) {
  4398. if ($v instanceof RedBean_OODBBean) {
  4399. $bean->removeProperty($p);
  4400. }
  4401. if (is_array($v)) {
  4402. if (strpos($p,'own')===0) {
  4403. $bean->removeProperty($p);
  4404. }
  4405. elseif (strpos($p,'shared')===0) {
  4406. $bean->removeProperty($p);
  4407. }
  4408. }
  4409. }
  4410. if (!$this->isFrozen) $this->check( $bean );
  4411. try {
  4412. $this->writer->selectRecord($bean->getMeta('type'),
  4413. array('id' => array( $bean->id) ),null,true );
  4414. }catch(RedBean_Exception_SQL $e) {
  4415. if (!$this->writer->sqlStateIn($e->getSQLState(),
  4416. array(
  4417. RedBean_QueryWriter::C_SQLSTATE_NO_SUCH_COLUMN,
  4418. RedBean_QueryWriter::C_SQLSTATE_NO_SUCH_TABLE)
  4419. )) throw $e;
  4420. }
  4421. $bean->id = 0;
  4422. $this->signal('after_delete', $bean );
  4423. }
  4424. /**
  4425. * Returns an array of beans. Pass a type and a series of ids and
  4426. * this method will bring you the correspondig beans.
  4427. *
  4428. * important note: Because this method loads beans using the load()
  4429. * function (but faster) it will return empty beans with ID 0 for
  4430. * every bean that could not be located. The resulting beans will have the
  4431. * passed IDs as their keys.
  4432. *
  4433. * @param string $type type of beans
  4434. * @param array $ids ids to load
  4435. *
  4436. * @return array $beans resulting beans (may include empty ones)
  4437. */
  4438. public function batch( $type, $ids ) {
  4439. if (!$ids) return array();
  4440. $collection = array();
  4441. try {
  4442. $rows = $this->writer->selectRecord($type,array('id'=>$ids));
  4443. }catch(RedBean_Exception_SQL $e) {
  4444. if (!$this->writer->sqlStateIn($e->getSQLState(),
  4445. array(
  4446. RedBean_QueryWriter::C_SQLSTATE_NO_SUCH_COLUMN,
  4447. RedBean_QueryWriter::C_SQLSTATE_NO_SUCH_TABLE)
  4448. )) throw $e;
  4449. $rows = false;
  4450. }
  4451. $this->stash = array();
  4452. if (!$rows) return array();
  4453. foreach($rows as $row) {
  4454. $this->stash[$row['id']] = $row;
  4455. }
  4456. foreach($ids as $id) {
  4457. $collection[ $id ] = $this->load( $type, $id );
  4458. }
  4459. $this->stash = NULL;
  4460. return $collection;
  4461. }
  4462. /**
  4463. * This is a convenience method; it converts database rows
  4464. * (arrays) into beans. Given a type and a set of rows this method
  4465. * will return an array of beans of the specified type loaded with
  4466. * the data fields provided by the result set from the database.
  4467. *
  4468. * @param string $type type of beans you would like to have
  4469. * @param array $rows rows from the database result
  4470. *
  4471. * @return array $collectionOfBeans collection of beans
  4472. */
  4473. public function convertToBeans($type, $rows) {
  4474. $collection = array();
  4475. $this->stash = array();
  4476. foreach($rows as $row) {
  4477. $id = $row['id'];
  4478. $this->stash[$id] = $row;
  4479. $collection[ $id ] = $this->load( $type, $id );
  4480. }
  4481. $this->stash = NULL;
  4482. return $collection;
  4483. }
  4484. /**
  4485. * Returns the number of beans we have in DB of a given type.
  4486. *
  4487. * @param string $type type of bean we are looking for
  4488. *
  4489. * @return integer $num number of beans found
  4490. */
  4491. public function count($type) {
  4492. try {
  4493. return (int) $this->writer->count($type);
  4494. }catch(RedBean_Exception_SQL $e) {
  4495. if (!$this->writer->sqlStateIn($e->getSQLState(),
  4496. array(RedBean_QueryWriter::C_SQLSTATE_NO_SUCH_TABLE)
  4497. )) throw $e;
  4498. }
  4499. return 0;
  4500. }
  4501. /**
  4502. * Trash all beans of a given type.
  4503. *
  4504. * @param string $type type
  4505. *
  4506. * @return boolean $yesNo whether we actually did some work or not..
  4507. */
  4508. public function wipe($type) {
  4509. try {
  4510. $this->writer->wipe($type);
  4511. return true;
  4512. }catch(RedBean_Exception_SQL $e) {
  4513. if (!$this->writer->sqlStateIn($e->getSQLState(),
  4514. array(RedBean_QueryWriter::C_SQLSTATE_NO_SUCH_TABLE)
  4515. )) throw $e;
  4516. return false;
  4517. }
  4518. }
  4519. /**
  4520. * Returns an Association Manager for use with OODB.
  4521. * A simple getter function to obtain a reference to the association manager used for
  4522. * storage and more.
  4523. *
  4524. * @throws Exception
  4525. * @return RedBean_AssociationManager $assoc Association Manager
  4526. */
  4527. public function getAssociationManager() {
  4528. if (!isset($this->assocManager)) throw new Exception('No association manager available.');
  4529. return $this->assocManager;
  4530. }
  4531. /**
  4532. * Sets the association manager instance to be used by this OODB.
  4533. * A simple setter function to set the association manager to be used for storage and
  4534. * more.
  4535. *
  4536. * @param RedBean_AssociationManager $assoc sets the association manager to be used
  4537. *
  4538. * @return void
  4539. */
  4540. public function setAssociationManager(RedBean_AssociationManager $assoc) {
  4541. $this->assocManager = $assoc;
  4542. }
  4543. public function setDepList($dep) {
  4544. $this->dep = $dep;
  4545. }
  4546. }
  4547. /**
  4548. * ToolBox
  4549. * Contains most important redbean tools
  4550. *
  4551. * @file RedBean/ToolBox.php
  4552. * @description The ToolBox acts as a resource locator for RedBean but can
  4553. * be integrated in larger resource locators (nested).
  4554. * It does not do anything more than just store the three most
  4555. * important RedBean resources (tools): the database adapter,
  4556. * the redbean core class (oodb) and the query writer.
  4557. * @author Gabor de Mooij and the RedBeanPHP community
  4558. * @license BSD/GPLv2
  4559. *
  4560. *
  4561. * copyright (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community.
  4562. * This source file is subject to the BSD/GPLv2 License that is bundled
  4563. * with this source code in the file license.txt.
  4564. */
  4565. class RedBean_ToolBox {
  4566. /**
  4567. * Reference to the RedBeanPHP OODB Object Database instance
  4568. * @var RedBean_OODB
  4569. */
  4570. protected $oodb;
  4571. /**
  4572. * Reference to the Query Writer
  4573. * @var RedBean_QueryWriter
  4574. */
  4575. protected $writer;
  4576. /**
  4577. * Reference to the database adapter
  4578. * @var RedBean_Adapter_DBAdapter
  4579. */
  4580. protected $adapter;
  4581. /**
  4582. * Constructor.
  4583. * The Constructor of the ToolBox takes three arguments: a RedBean_OODB $redbean
  4584. * object database, a RedBean_Adapter $databaseAdapter and a
  4585. * RedBean_QueryWriter $writer. It stores these objects inside and acts as
  4586. * a micro service locator. You can pass the toolbox to any object that needs
  4587. * one of the RedBean core objects to interact with.
  4588. *
  4589. * @param RedBean_OODB $oodb Object Database
  4590. * @param RedBean_Adapter_DBAdapter $adapter Adapter
  4591. * @param RedBean_QueryWriter $writer Writer
  4592. *
  4593. * return RedBean_ToolBox $toolbox Toolbox
  4594. */
  4595. public function __construct(RedBean_OODB $oodb,RedBean_Adapter $adapter,RedBean_QueryWriter $writer) {
  4596. $this->oodb = $oodb;
  4597. $this->adapter = $adapter;
  4598. $this->writer = $writer;
  4599. return $this;
  4600. }
  4601. /**
  4602. * The Toolbox acts as a kind of micro service locator, providing just the
  4603. * most important objects that make up RedBean. You can pass the toolkit to
  4604. * any object that needs one of these objects to function properly.
  4605. * Returns the QueryWriter; normally you do not use this object but other
  4606. * object might want to use the default RedBean query writer to be
  4607. * database independent.
  4608. *
  4609. * @return RedBean_QueryWriter $writer writer
  4610. */
  4611. public function getWriter() {
  4612. return $this->writer;
  4613. }
  4614. /**
  4615. * The Toolbox acts as a kind of micro service locator, providing just the
  4616. * most important objects that make up RedBean. You can pass the toolkit to
  4617. * any object that needs one of these objects to function properly.
  4618. * Retruns the RedBean OODB Core object. The RedBean OODB object is
  4619. * the ultimate core of Redbean. It provides the means to store and load
  4620. * beans. Extract this object immediately after invoking a kickstart method.
  4621. *
  4622. * @return RedBean_OODB $oodb Object Database
  4623. */
  4624. public function getRedBean() {
  4625. return $this->oodb;
  4626. }
  4627. /**
  4628. * The Toolbox acts as a kind of micro service locator, providing just the
  4629. * most important objects that make up RedBean. You can pass the toolkit to
  4630. * any object that needs one of these objects to function properly.
  4631. * Returns the adapter. The Adapter can be used to perform queries
  4632. * on the database directly.
  4633. *
  4634. * @return RedBean_Adapter_DBAdapter $adapter Adapter
  4635. */
  4636. public function getDatabaseAdapter() {
  4637. return $this->adapter;
  4638. }
  4639. }
  4640. /**
  4641. * RedBean Association
  4642. *
  4643. * @file RedBean/AssociationManager.php
  4644. * @description Manages simple bean associations.
  4645. *
  4646. * @author Gabor de Mooij and the RedBeanPHP Community
  4647. * @license BSD/GPLv2
  4648. *
  4649. * (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community.
  4650. * This source file is subject to the BSD/GPLv2 License that is bundled
  4651. * with this source code in the file license.txt.
  4652. */
  4653. class RedBean_AssociationManager extends RedBean_Observable {
  4654. /**
  4655. * Contains a reference to the Object Database OODB
  4656. * @var RedBean_OODB
  4657. */
  4658. protected $oodb;
  4659. /**
  4660. * Contains a reference to the Database Adapter
  4661. * @var RedBean_Adapter_DBAdapter
  4662. */
  4663. protected $adapter;
  4664. /**
  4665. * Contains a reference to the Query Writer
  4666. * @var RedBean_QueryWriter
  4667. */
  4668. protected $writer;
  4669. /**
  4670. * Constructor
  4671. *
  4672. * @param RedBean_ToolBox $tools toolbox
  4673. */
  4674. public function __construct( RedBean_ToolBox $tools ) {
  4675. $this->oodb = $tools->getRedBean();
  4676. $this->adapter = $tools->getDatabaseAdapter();
  4677. $this->writer = $tools->getWriter();
  4678. $this->toolbox = $tools;
  4679. }
  4680. /**
  4681. * Creates a table name based on a types array.
  4682. * Manages the get the correct name for the linking table for the
  4683. * types provided.
  4684. *
  4685. * @todo find a nice way to decouple this class from QueryWriter?
  4686. *
  4687. * @param array $types 2 types as strings
  4688. *
  4689. * @return string $table table
  4690. */
  4691. public function getTable( $types ) {
  4692. return RedBean_QueryWriter_AQueryWriter::getAssocTableFormat($types);
  4693. }
  4694. /**
  4695. * Associates two beans with eachother using a many-to-many relation.
  4696. *
  4697. * @param RedBean_OODBBean $bean1 bean1
  4698. * @param RedBean_OODBBean $bean2 bean2
  4699. */
  4700. public function associate(RedBean_OODBBean $bean1, RedBean_OODBBean $bean2) {
  4701. $table = $this->getTable( array($bean1->getMeta('type') , $bean2->getMeta('type')) );
  4702. $bean = $this->oodb->dispense($table);
  4703. return $this->associateBeans( $bean1, $bean2, $bean );
  4704. }
  4705. /**
  4706. * Associates a pair of beans. This method associates two beans, no matter
  4707. * what types.Accepts a base bean that contains data for the linking record.
  4708. *
  4709. * @param RedBean_OODBBean $bean1 first bean
  4710. * @param RedBean_OODBBean $bean2 second bean
  4711. * @param RedBean_OODBBean $bean base bean
  4712. *
  4713. * @return mixed $id either the link ID or null
  4714. */
  4715. protected function associateBeans(RedBean_OODBBean $bean1, RedBean_OODBBean $bean2, RedBean_OODBBean $bean) {
  4716. $property1 = $bean1->getMeta('type') . '_id';
  4717. $property2 = $bean2->getMeta('type') . '_id';
  4718. if ($property1==$property2) $property2 = $bean2->getMeta('type').'2_id';
  4719. //add a build command for Unique Indexes
  4720. $bean->setMeta('buildcommand.unique' , array(array($property1, $property2)));
  4721. //add a build command for Single Column Index (to improve performance in case unqiue cant be used)
  4722. $indexName1 = 'index_for_'.$bean->getMeta('type').'_'.$property1;
  4723. $indexName2 = 'index_for_'.$bean->getMeta('type').'_'.$property2;
  4724. $bean->setMeta('buildcommand.indexes', array($property1=>$indexName1,$property2=>$indexName2));
  4725. $this->oodb->store($bean1);
  4726. $this->oodb->store($bean2);
  4727. $bean->setMeta("cast.$property1","id");
  4728. $bean->setMeta("cast.$property2","id");
  4729. $bean->$property1 = $bean1->id;
  4730. $bean->$property2 = $bean2->id;
  4731. try {
  4732. $id = $this->oodb->store( $bean );
  4733. //On creation, add constraints....
  4734. if (!$this->oodb->isFrozen() &&
  4735. $bean->getMeta('buildreport.flags.created')){
  4736. $bean->setMeta('buildreport.flags.created',0);
  4737. if (!$this->oodb->isFrozen())
  4738. $this->writer->addConstraint( $bean1, $bean2 );
  4739. }
  4740. return $id;
  4741. }
  4742. catch(RedBean_Exception_SQL $e) {
  4743. if (!$this->writer->sqlStateIn($e->getSQLState(),
  4744. array(
  4745. RedBean_QueryWriter::C_SQLSTATE_INTEGRITY_CONSTRAINT_VIOLATION
  4746. ))) throw $e;
  4747. }
  4748. }
  4749. /**
  4750. * Returns all ids of beans of type $type that are related to $bean. If the
  4751. * $getLinks parameter is set to boolean TRUE this method will return the ids
  4752. * of the association beans instead. You can also add additional SQL. This SQL
  4753. * will be appended to the original query string used by this method. Note that this
  4754. * method will not return beans, just keys. For a more convenient method see the R-facade
  4755. * method related(), that is in fact a wrapper for this method that offers a more
  4756. * convenient solution. If you want to make use of this method, consider the
  4757. * OODB batch() method to convert the ids to beans.
  4758. *
  4759. * Since 3.2, you can now also pass an array of beans instead just one
  4760. * bean as the first parameter.
  4761. *
  4762. * @throws RedBean_Exception_SQL
  4763. *
  4764. * @param RedBean_OODBBean|array $bean reference bean
  4765. * @param string $type target type
  4766. * @param bool $getLinks whether you are interested in the assoc records
  4767. * @param bool $sql room for additional SQL
  4768. *
  4769. * @return array $ids
  4770. */
  4771. public function related( $bean, $type, $getLinks=false, $sql=false) {
  4772. if (!is_array($bean) && !($bean instanceof RedBean_OODBBean)) throw new RedBean_Exception_Security('Expected array or RedBean_OODBBean but got:'.gettype($bean));
  4773. $ids = array();
  4774. if (is_array($bean)) {
  4775. $beans = $bean;
  4776. foreach($beans as $b) {
  4777. if (!($b instanceof RedBean_OODBBean)) throw new RedBean_Exception_Security('Expected RedBean_OODBBean in array but got:'.gettype($b));
  4778. $ids[] = $b->id;
  4779. }
  4780. $bean = reset($beans);
  4781. }
  4782. else $ids[] = $bean->id;
  4783. $table = $this->getTable( array($bean->getMeta('type') , $type) );
  4784. if ($type==$bean->getMeta('type')) {
  4785. $type .= '2';
  4786. $cross = 1;
  4787. }
  4788. else $cross=0;
  4789. if (!$getLinks) $targetproperty = $type.'_id'; else $targetproperty='id';
  4790. $property = $bean->getMeta('type').'_id';
  4791. try {
  4792. $sqlFetchKeys = $this->writer->selectRecord(
  4793. $table,
  4794. array( $property => $ids ),
  4795. $sql,
  4796. false
  4797. );
  4798. $sqlResult = array();
  4799. foreach( $sqlFetchKeys as $row ) {
  4800. $sqlResult[] = $row[$targetproperty];
  4801. }
  4802. if ($cross) {
  4803. $sqlFetchKeys2 = $this->writer->selectRecord(
  4804. $table,
  4805. array( $targetproperty => $ids),
  4806. $sql,
  4807. false
  4808. );
  4809. foreach( $sqlFetchKeys2 as $row ) {
  4810. $sqlResult[] = $row[$property];
  4811. }
  4812. }
  4813. return $sqlResult; //or returns rows in case of $sql != empty
  4814. }catch(RedBean_Exception_SQL $e) {
  4815. if (!$this->writer->sqlStateIn($e->getSQLState(),
  4816. array(
  4817. RedBean_QueryWriter::C_SQLSTATE_NO_SUCH_COLUMN,
  4818. RedBean_QueryWriter::C_SQLSTATE_NO_SUCH_TABLE)
  4819. )) throw $e;
  4820. return array();
  4821. }
  4822. }
  4823. /**
  4824. * Breaks the association between two beans. This method unassociates two beans. If the
  4825. * method succeeds the beans will no longer form an association. In the database
  4826. * this means that the association record will be removed. This method uses the
  4827. * OODB trash() method to remove the association links, thus giving FUSE models the
  4828. * opportunity to hook-in additional business logic. If the $fast parameter is
  4829. * set to boolean TRUE this method will remove the beans without their consent,
  4830. * bypassing FUSE. This can be used to improve performance.
  4831. *
  4832. * @param RedBean_OODBBean $bean1 first bean
  4833. * @param RedBean_OODBBean $bean2 second bean
  4834. * @param boolean $fast If TRUE, removes the entries by query without FUSE
  4835. */
  4836. public function unassociate(RedBean_OODBBean $bean1, RedBean_OODBBean $bean2, $fast=null) {
  4837. $this->oodb->store($bean1);
  4838. $this->oodb->store($bean2);
  4839. $table = $this->getTable( array($bean1->getMeta('type') , $bean2->getMeta('type')) );
  4840. $type = $bean1->getMeta('type');
  4841. if ($type==$bean2->getMeta('type')) {
  4842. $type .= '2';
  4843. $cross = 1;
  4844. }
  4845. else $cross = 0;
  4846. $property1 = $type.'_id';
  4847. $property2 = $bean2->getMeta('type').'_id';
  4848. $value1 = (int) $bean1->id;
  4849. $value2 = (int) $bean2->id;
  4850. try {
  4851. $rows = $this->writer->selectRecord($table,array(
  4852. $property1 => array($value1), $property2=>array($value2)),null,$fast
  4853. );
  4854. if ($cross) {
  4855. $rows2 = $this->writer->selectRecord($table,array(
  4856. $property2 => array($value1), $property1=>array($value2)),null,$fast
  4857. );
  4858. if ($fast) return;
  4859. $rows = array_merge($rows,$rows2);
  4860. }
  4861. if ($fast) return;
  4862. $beans = $this->oodb->convertToBeans($table,$rows);
  4863. foreach($beans as $link) {
  4864. $this->oodb->trash($link);
  4865. }
  4866. }catch(RedBean_Exception_SQL $e) {
  4867. if (!$this->writer->sqlStateIn($e->getSQLState(),
  4868. array(
  4869. RedBean_QueryWriter::C_SQLSTATE_NO_SUCH_COLUMN,
  4870. RedBean_QueryWriter::C_SQLSTATE_NO_SUCH_TABLE)
  4871. )) throw $e;
  4872. }
  4873. return;
  4874. }
  4875. /**
  4876. * Removes all relations for a bean. This method breaks every connection between
  4877. * a certain bean $bean and every other bean of type $type. Warning: this method
  4878. * is really fast because it uses a direct SQL query however it does not inform the
  4879. * models about this. If you want to notify FUSE models about deletion use a foreach-loop
  4880. * with unassociate() instead. (that might be slower though)
  4881. *
  4882. * @param RedBean_OODBBean $bean reference bean
  4883. * @param string $type type of beans that need to be unassociated
  4884. *
  4885. * @return void
  4886. */
  4887. public function clearRelations(RedBean_OODBBean $bean, $type) {
  4888. $this->oodb->store($bean);
  4889. $table = $this->getTable( array($bean->getMeta('type') , $type) );
  4890. if ($type==$bean->getMeta('type')) {
  4891. $property2 = $type.'2_id';
  4892. $cross = 1;
  4893. }
  4894. else $cross = 0;
  4895. $property = $bean->getMeta('type').'_id';
  4896. try {
  4897. $this->writer->selectRecord( $table, array($property=>array($bean->id)),null,true);
  4898. if ($cross) {
  4899. $this->writer->selectRecord( $table, array($property2=>array($bean->id)),null,true);
  4900. }
  4901. }catch(RedBean_Exception_SQL $e) {
  4902. if (!$this->writer->sqlStateIn($e->getSQLState(),
  4903. array(
  4904. RedBean_QueryWriter::C_SQLSTATE_NO_SUCH_COLUMN,
  4905. RedBean_QueryWriter::C_SQLSTATE_NO_SUCH_TABLE)
  4906. )) throw $e;
  4907. }
  4908. }
  4909. /**
  4910. * Given two beans this function returns TRUE if they are associated using a
  4911. * many-to-many association, FALSE otherwise.
  4912. *
  4913. * @throws RedBean_Exception_SQL
  4914. *
  4915. * @param RedBean_OODBBean $bean1 bean
  4916. * @param RedBean_OODBBean $bean2 bean
  4917. *
  4918. * @return bool $related whether they are associated N-M
  4919. */
  4920. public function areRelated(RedBean_OODBBean $bean1, RedBean_OODBBean $bean2) {
  4921. if (!$bean1->getID() || !$bean2->getID()) return false;
  4922. $table = $this->getTable( array($bean1->getMeta('type') , $bean2->getMeta('type')) );
  4923. $type = $bean1->getMeta('type');
  4924. if ($type==$bean2->getMeta('type')) {
  4925. $type .= '2';
  4926. $cross = 1;
  4927. }
  4928. else $cross = 0;
  4929. $property1 = $type.'_id';
  4930. $property2 = $bean2->getMeta('type').'_id';
  4931. $value1 = (int) $bean1->id;
  4932. $value2 = (int) $bean2->id;
  4933. try {
  4934. $rows = $this->writer->selectRecord($table,array(
  4935. $property1 => array($value1), $property2=>array($value2)),null
  4936. );
  4937. if ($cross) {
  4938. $rows2 = $this->writer->selectRecord($table,array(
  4939. $property2 => array($value1), $property1=>array($value2)),null
  4940. );
  4941. $rows = array_merge($rows,$rows2);
  4942. }
  4943. }catch(RedBean_Exception_SQL $e) {
  4944. if (!$this->writer->sqlStateIn($e->getSQLState(),
  4945. array(
  4946. RedBean_QueryWriter::C_SQLSTATE_NO_SUCH_COLUMN,
  4947. RedBean_QueryWriter::C_SQLSTATE_NO_SUCH_TABLE)
  4948. )) throw $e;
  4949. return false;
  4950. }
  4951. return (count($rows)>0);
  4952. }
  4953. }
  4954. /**
  4955. * RedBean Extended Association
  4956. *
  4957. * @file RedBean/ExtAssociationManager.php
  4958. * @description Manages complex bean associations.
  4959. * @author Gabor de Mooij and the RedBeanPHP Community
  4960. * @license BSD/GPLv2
  4961. *
  4962. * copyright (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community
  4963. * This source file is subject to the BSD/GPLv2 License that is bundled
  4964. * with this source code in the file license.txt.
  4965. */
  4966. class RedBean_ExtAssociationManager extends RedBean_AssociationManager {
  4967. /**
  4968. * Associates two beans with eachother. This method connects two beans with eachother, just
  4969. * like the other associate() method in the Association Manager. The difference is however
  4970. * that this method accepts a base bean, this bean will be used as the basis of the
  4971. * association record in the link table. You can thus add additional properties and
  4972. * even foreign keys.
  4973. *
  4974. * @param RedBean_OODBBean $bean1 bean 1
  4975. * @param RedBean_OODBBean $bean2 bean 2
  4976. * @param RedBean_OODBBean $bbean base bean for association record
  4977. *
  4978. * @return void
  4979. */
  4980. public function extAssociate(RedBean_OODBBean $bean1, RedBean_OODBBean $bean2, RedBean_OODBBean $baseBean ) {
  4981. $table = $this->getTable( array($bean1->getMeta('type') , $bean2->getMeta('type')) );
  4982. $baseBean->setMeta('type', $table );
  4983. return $this->associateBeans( $bean1, $bean2, $baseBean );
  4984. }
  4985. }
  4986. /**
  4987. * RedBean Setup
  4988. * Helper class to quickly setup RedBean for you.
  4989. *
  4990. * @file RedBean/Setup.php
  4991. * @description Helper class to quickly setup RedBean for you
  4992. *
  4993. * @author Gabor de Mooij and the RedBeanPHP community
  4994. * @license BSD/GPLv2
  4995. *
  4996. *
  4997. * copyright (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community
  4998. * This source file is subject to the BSD/GPLv2 License that is bundled
  4999. * with this source code in the file license.txt.
  5000. */
  5001. class RedBean_Setup {
  5002. /**
  5003. * This method checks the DSN string. If the DSN string contains a
  5004. * database name that is not supported by RedBean yet then it will
  5005. * throw an exception RedBean_Exception_NotImplemented. In any other
  5006. * case this method will just return boolean TRUE.
  5007. * @throws RedBean_Exception_NotImplemented
  5008. * @param string $dsn
  5009. * @return boolean $true
  5010. */
  5011. private static function checkDSN($dsn) {
  5012. $dsn = trim($dsn);
  5013. $dsn = strtolower($dsn);
  5014. if (
  5015. strpos($dsn, 'mysql:')!==0
  5016. && strpos($dsn,'sqlite:')!==0
  5017. && strpos($dsn,'pgsql:')!==0
  5018. && strpos($dsn,'cubrid:')!==0
  5019. ) {
  5020. trigger_error('Unsupported DSN');
  5021. }
  5022. else {
  5023. return true;
  5024. }
  5025. }
  5026. /**
  5027. * Generic Kickstart method.
  5028. * This is the generic kickstarter. It will prepare a database connection
  5029. * using the $dsn, the $username and the $password you provide.
  5030. * If $frozen is boolean TRUE it will start RedBean in frozen mode, meaning
  5031. * that the database cannot be altered. If RedBean is started in fluid mode
  5032. * it will adjust the schema of the database if it detects an
  5033. * incompatible bean.
  5034. * This method returns a RedBean_Toolbox $toolbox filled with a
  5035. * RedBean_Adapter, a RedBean_QueryWriter and most importantly a
  5036. * RedBean_OODB; the object database. To start storing beans in the database
  5037. * simply say: $redbean = $toolbox->getRedBean(); Now you have a reference
  5038. * to the RedBean object.
  5039. * Optionally instead of using $dsn you may use an existing PDO connection.
  5040. * Example: RedBean_Setup::kickstart($existingConnection, true);
  5041. *
  5042. * @param string|PDO $dsn Database Connection String (or PDO instance)
  5043. * @param string $username Username for database
  5044. * @param string $password Password for database
  5045. * @param boolean $frozen Start in frozen mode?
  5046. *
  5047. * @return RedBean_ToolBox $toolbox
  5048. */
  5049. public static function kickstart($dsn,$username=NULL,$password=NULL,$frozen=false ) {
  5050. if ($dsn instanceof PDO) {
  5051. $pdo = new RedBean_Driver_PDO($dsn);
  5052. $dsn = $pdo->getDatabaseType() ;
  5053. }
  5054. else {
  5055. self::checkDSN($dsn);
  5056. $pdo = new RedBean_Driver_PDO($dsn,$username,$password);
  5057. }
  5058. $adapter = new RedBean_Adapter_DBAdapter($pdo);
  5059. if (strpos($dsn,'pgsql')===0) {
  5060. $writer = new RedBean_QueryWriter_PostgreSQL($adapter);
  5061. }
  5062. else if (strpos($dsn,'sqlite')===0) {
  5063. $writer = new RedBean_QueryWriter_SQLiteT($adapter);
  5064. }
  5065. else if (strpos($dsn,'cubrid')===0) {
  5066. $writer = new RedBean_QueryWriter_Cubrid($adapter);
  5067. }
  5068. else {
  5069. $writer = new RedBean_QueryWriter_MySQL($adapter);
  5070. }
  5071. $redbean = new RedBean_OODB($writer);
  5072. if ($frozen) $redbean->freeze(true);
  5073. $toolbox = new RedBean_ToolBox($redbean,$adapter,$writer);
  5074. //deliver everything back in a neat toolbox
  5075. return $toolbox;
  5076. }
  5077. }
  5078. /**
  5079. * RedBean interface for Model Formatting - Part of FUSE
  5080. *
  5081. * @file RedBean/ModelFormatter.php
  5082. * @description RedBean IModelFormatter
  5083. *
  5084. * @author Gabor de Mooij and the RedBeanPHP Community
  5085. * @license BSD/GPLv2
  5086. *
  5087. *
  5088. * copyright (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community
  5089. * This source file is subject to the BSD/GPLv2 License that is bundled
  5090. * with this source code in the file license.txt.
  5091. */
  5092. interface RedBean_IModelFormatter {
  5093. /**
  5094. * ModelHelper will call this method of the class
  5095. * you provide to discover the model
  5096. *
  5097. * @param string $model
  5098. *
  5099. * @return string $formattedModel
  5100. */
  5101. public function formatModel( $model );
  5102. }
  5103. /**
  5104. * RedBean interface for Logging
  5105. *
  5106. * @name RedBean ILogger
  5107. * @file RedBean/ILogger.php
  5108. * @author Gabor de Mooij
  5109. * @license BSD
  5110. *
  5111. *
  5112. * copyright (c) G.J.G.T. (Gabor) de Mooij
  5113. * This source file is subject to the BSD/GPLv2 License that is bundled
  5114. * with this source code in the file license.txt.
  5115. */
  5116. interface RedBean_ILogger {
  5117. /**
  5118. * Redbean will call this method to log your data
  5119. *
  5120. * @param ...
  5121. */
  5122. public function log();
  5123. }
  5124. /**
  5125. * RedBean class for Logging
  5126. *
  5127. * @name RedBean ILogger
  5128. * @file RedBean/ILogger.php
  5129. * @author Gabor de Mooij
  5130. * @license BSD
  5131. *
  5132. *
  5133. * copyright (c) G.J.G.T. (Gabor) de Mooij
  5134. * This source file is subject to the BSD/GPLv2 License that is bundled
  5135. * with this source code in the file license.txt.
  5136. */
  5137. class RedBean_Logger implements RedBean_ILogger {
  5138. /**
  5139. * Default logger method logging to STDOUT
  5140. *
  5141. * @param ...
  5142. */
  5143. public function log() {
  5144. if (func_num_args() > 0) {
  5145. foreach (func_get_args() as $argument) {
  5146. if (is_array($argument)) echo print_r($argument,true); else echo $argument;
  5147. echo "<br>\n";
  5148. }
  5149. }
  5150. }
  5151. }
  5152. /**
  5153. * RedBean Bean Helper Interface
  5154. *
  5155. * @file RedBean/IBeanHelper.php
  5156. * @description Interface for Bean Helper.
  5157. * A little bolt that glues the whole machinery together.
  5158. *
  5159. * @author Gabor de Mooij and the RedBeanPHP Community
  5160. * @license BSD/GPLv2
  5161. *
  5162. * copyright (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community
  5163. * This source file is subject to the BSD/GPLv2 License that is bundled
  5164. * with this source code in the file license.txt.
  5165. *
  5166. */
  5167. interface RedBean_IBeanHelper {
  5168. /**
  5169. * @abstract
  5170. * @return RedBean_Toolbox $toolbox toolbox
  5171. */
  5172. public function getToolbox();
  5173. }
  5174. /**
  5175. * RedBean Bean Helper
  5176. * @file RedBean/BeanHelperFacade.php
  5177. * @description Finds the toolbox for the bean.
  5178. * @author Gabor de Mooij and the RedBeanPHP Community
  5179. * @license BSD/GPLv2
  5180. *
  5181. * (c) copyright G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community
  5182. * This source file is subject to the BSD/GPLv2 License that is bundled
  5183. * with this source code in the file license.txt.
  5184. */
  5185. class RedBean_BeanHelperFacade implements RedBean_IBeanHelper {
  5186. /**
  5187. * Returns a reference to the toolbox. This method returns a toolbox
  5188. * for beans that need to use toolbox functions. Since beans can contain
  5189. * lists they need a toolbox to lazy-load their relationships.
  5190. *
  5191. * @return RedBean_ToolBox $toolbox toolbox containing all kinds of goodies
  5192. */
  5193. public function getToolbox() {
  5194. return RedBean_Facade::$toolbox;
  5195. }
  5196. }
  5197. /**
  5198. * SimpleModel
  5199. *
  5200. * @file RedBean/SimpleModel.php
  5201. * @description Part of FUSE
  5202. * @author Gabor de Mooij and the RedBeanPHP Team
  5203. * @license BSD/GPLv2
  5204. *
  5205. * copyright (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community
  5206. * This source file is subject to the BSD/GPLv2 License that is bundled
  5207. * with this source code in the file license.txt.
  5208. */
  5209. class RedBean_SimpleModel {
  5210. /**
  5211. * Contains the inner bean.
  5212. * @var RedBean_OODBBean
  5213. */
  5214. protected $bean;
  5215. /**
  5216. * Used by FUSE: the ModelHelper class to connect a bean to a model.
  5217. * This method loads a bean in the model.
  5218. *
  5219. * @param RedBean_OODBBean $bean bean
  5220. */
  5221. public function loadBean( RedBean_OODBBean $bean ) {
  5222. $this->bean = $bean;
  5223. }
  5224. /**
  5225. * Magic Getter to make the bean properties available from
  5226. * the $this-scope.
  5227. *
  5228. * @param string $prop property
  5229. *
  5230. * @return mixed $propertyValue value
  5231. */
  5232. public function __get( $prop ) {
  5233. return $this->bean->$prop;
  5234. }
  5235. /**
  5236. * Magic Setter
  5237. *
  5238. * @param string $prop property
  5239. * @param mixed $value value
  5240. */
  5241. public function __set( $prop, $value ) {
  5242. $this->bean->$prop = $value;
  5243. }
  5244. /**
  5245. * Isset implementation
  5246. *
  5247. * @param string $key key
  5248. *
  5249. * @return
  5250. */
  5251. public function __isset($key) {
  5252. return (isset($this->bean->$key));
  5253. }
  5254. /**
  5255. * Box the bean using the current model.
  5256. *
  5257. * @return RedBean_SimpleModel $box a bean in a box
  5258. */
  5259. public function box() {
  5260. return $this;
  5261. }
  5262. /**
  5263. * Unbox the bean from the model.
  5264. *
  5265. * @return RedBean_OODBBean $bean bean
  5266. */
  5267. public function unbox(){
  5268. return $this->bean;
  5269. }
  5270. }
  5271. /**
  5272. * RedBean Model Helper
  5273. *
  5274. * @file RedBean/ModelHelper.php
  5275. * @description Connects beans to models, in essence
  5276. * this is the core of so-called FUSE.
  5277. *
  5278. * @author Gabor de Mooij and the RedBeanPHP Community
  5279. * @license BSD/GPLv2
  5280. *
  5281. * copyright (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community
  5282. * This source file is subject to the BSD/GPLv2 License that is bundled
  5283. * with this source code in the file license.txt.
  5284. *
  5285. */
  5286. class RedBean_ModelHelper implements RedBean_Observer {
  5287. /**
  5288. * Holds a model formatter
  5289. * @var RedBean_IModelFormatter
  5290. */
  5291. private static $modelFormatter;
  5292. /**
  5293. * Holds a dependency injector
  5294. * @var type
  5295. */
  5296. private static $dependencyInjector;
  5297. /**
  5298. * Connects OODB to a model if a model exists for that
  5299. * type of bean. This connector is used in the facade.
  5300. *
  5301. * @param string $eventName
  5302. * @param RedBean_OODBBean $bean
  5303. */
  5304. public function onEvent( $eventName, $bean ) {
  5305. $bean->$eventName();
  5306. }
  5307. /**
  5308. * Given a model ID (model identifier) this method returns the
  5309. * full model name.
  5310. *
  5311. * @param string $model
  5312. * @param RedBean_OODBBean $bean
  5313. *
  5314. * @return string $fullname
  5315. */
  5316. public static function getModelName( $model, $bean = null ) {
  5317. if (self::$modelFormatter){
  5318. return self::$modelFormatter->formatModel($model,$bean);
  5319. }
  5320. else {
  5321. return 'Model_'.ucfirst($model);
  5322. }
  5323. }
  5324. /**
  5325. * Sets the model formatter to be used to discover a model
  5326. * for Fuse.
  5327. *
  5328. * @param string $modelFormatter
  5329. */
  5330. public static function setModelFormatter( $modelFormatter ) {
  5331. self::$modelFormatter = $modelFormatter;
  5332. }
  5333. /**
  5334. * Obtains a new instance of $modelClassName, using a dependency injection
  5335. * container if possible.
  5336. *
  5337. * @param string $modelClassName name of the model
  5338. */
  5339. public static function factory( $modelClassName ) {
  5340. if (self::$dependencyInjector) {
  5341. return self::$dependencyInjector->getInstance($modelClassName);
  5342. }
  5343. return new $modelClassName();
  5344. }
  5345. /**
  5346. * Sets the dependency injector to be used.
  5347. *
  5348. * @param RedBean_DependencyInjector $di injecto to be used
  5349. */
  5350. public static function setDependencyInjector( RedBean_DependencyInjector $di ) {
  5351. self::$dependencyInjector = $di;
  5352. }
  5353. /**
  5354. * Stops the dependency injector from resolving dependencies. Removes the
  5355. * reference to the dependency injector.
  5356. */
  5357. public static function clearDependencyInjector() {
  5358. self::$dependencyInjector = null;
  5359. }
  5360. }
  5361. /**
  5362. * RedBean SQL Helper
  5363. *
  5364. * @file RedBean/SQLHelper.php
  5365. * @description Allows you to mix PHP and SQL as if they were
  5366. * a unified language
  5367. *
  5368. * Simplest case:
  5369. *
  5370. * $r->now(); //returns SQL time
  5371. *
  5372. *
  5373. * Another Example:
  5374. *
  5375. * $f->begin()
  5376. * ->select('*')
  5377. * ->from('island')->where('id = ? ')->put(1)->get();
  5378. *
  5379. * Another example:
  5380. *
  5381. * $f->begin()->show('tables')->get('col');
  5382. *
  5383. *
  5384. * @author Gabor de Mooij and the RedBeanPHP community
  5385. * @license BSD/GPLv2
  5386. *
  5387. *
  5388. * copyright (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community.
  5389. * This source file is subject to the BSD/GPLv2 License that is bundled
  5390. * with this source code in the file license.txt.
  5391. */
  5392. class RedBean_SQLHelper {
  5393. /**
  5394. * Holds the database adapter for executing SQL queries.
  5395. * @var RedBean_Adapter
  5396. */
  5397. protected $adapter;
  5398. /**
  5399. * Holds current mode
  5400. * @var boolean
  5401. */
  5402. protected $capture = false;
  5403. /**
  5404. * Holds SQL until now
  5405. * @var string
  5406. */
  5407. protected $sql = '';
  5408. /**
  5409. * Holds list of parameters for SQL Query
  5410. * @var array
  5411. */
  5412. protected $params = array();
  5413. /**
  5414. * Constructor
  5415. *
  5416. * @param RedBean_DBAdapter $adapter database adapter for querying
  5417. */
  5418. public function __construct(RedBean_Adapter $adapter) {
  5419. $this->adapter = $adapter;
  5420. }
  5421. /**
  5422. * Magic method to construct SQL query
  5423. *
  5424. * @param string $funcName name of the next SQL statement/keyword
  5425. * @param array $args list of statements to be seperated by commas
  5426. *
  5427. * @return mixed $result either self or result depending on mode
  5428. */
  5429. public function __call($funcName,$args=array()) {
  5430. $funcName = str_replace('_',' ',$funcName);
  5431. if ($this->capture) {
  5432. $this->sql .= ' '.$funcName . ' '.implode(',', $args);
  5433. return $this;
  5434. }
  5435. else {
  5436. return $this->adapter->getCell('SELECT '.$funcName.'('.implode(',',$args).')');
  5437. }
  5438. }
  5439. /**
  5440. * Begins SQL query
  5441. *
  5442. * @return RedBean_SQLHelper $this chainable
  5443. */
  5444. public function begin() {
  5445. $this->capture = true;
  5446. return $this;
  5447. }
  5448. /**
  5449. * Adds a value to the parameter list
  5450. *
  5451. * @param mixed $param parameter to be added
  5452. *
  5453. * @return RedBean_SQLHelper $this chainable
  5454. */
  5455. public function put($param) {
  5456. $this->params[] = $param;
  5457. return $this;
  5458. }
  5459. /**
  5460. * Executes query and returns result
  5461. *
  5462. * @return mixed $result
  5463. */
  5464. public function get($what='') {
  5465. $what = 'get'.ucfirst($what);
  5466. $rs = $this->adapter->$what($this->sql,$this->params);
  5467. $this->clear();
  5468. return $rs;
  5469. }
  5470. /**
  5471. * Clears the parameter list as well as the SQL query string.
  5472. *
  5473. * @return RedBean_SQLHelper $this chainable
  5474. */
  5475. public function clear() {
  5476. $this->sql = '';
  5477. $this->params = array();
  5478. $this->capture = false; //turn off capture mode (issue #142)
  5479. return $this;
  5480. }
  5481. /**
  5482. * To explicitly add a piece of SQL.
  5483. *
  5484. * @param string $sql sql
  5485. *
  5486. * @return RedBean_SQLHelper
  5487. */
  5488. public function addSQL($sql) {
  5489. if ($this->capture) {
  5490. $this->sql .= ' '.$sql . ' ';
  5491. return $this;
  5492. }
  5493. }
  5494. /**
  5495. * Returns query parts.
  5496. *
  5497. * @return array $queryParts query parts.
  5498. */
  5499. public function getQuery() {
  5500. $list = array($this->sql,$this->params);
  5501. $this->clear();
  5502. return $list;
  5503. }
  5504. }
  5505. /**
  5506. * RedBean Tag Manager
  5507. *
  5508. * @file RedBean/TagManager.php
  5509. * @description RedBean Tag Manager
  5510. *
  5511. * @author Gabor de Mooij and the RedBeanPHP community
  5512. * @license BSD/GPLv2
  5513. *
  5514. * Provides methods to tag beans and perform tag-based searches in the
  5515. * bean database.
  5516. *
  5517. * copyright (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community
  5518. * This source file is subject to the BSD/GPLv2 License that is bundled
  5519. * with this source code in the file license.txt.
  5520. */
  5521. class RedBean_TagManager {
  5522. /**
  5523. * The Tag Manager requires a toolbox
  5524. * @var RedBean_Toolbox
  5525. */
  5526. protected $toolbox;
  5527. /**
  5528. * Association Manager to manage tag-bean relations
  5529. * @var RedBean_AssociationManager
  5530. */
  5531. protected $associationManager;
  5532. /**
  5533. * RedBeanPHP OODB instance
  5534. * @var RedBean_OODBBean
  5535. */
  5536. protected $redbean;
  5537. /**
  5538. * Constructor,
  5539. * creates a new instance of TagManager.
  5540. * @param RedBean_Toolbox $toolbox
  5541. */
  5542. public function __construct( RedBean_Toolbox $toolbox ) {
  5543. $this->toolbox = $toolbox;
  5544. $this->redbean = $toolbox->getRedBean();
  5545. $this->associationManager = $this->redbean->getAssociationManager();
  5546. }
  5547. /**
  5548. * Finds a tag bean by it's title.
  5549. *
  5550. * @param string $title title
  5551. *
  5552. * @return RedBean_OODBBean $bean | null
  5553. */
  5554. public function findTagByTitle($title) {
  5555. $beans = $this->redbean->find('tag',array('title'=>array($title)));
  5556. if ($beans) {
  5557. return reset($beans);
  5558. }
  5559. return null;
  5560. }
  5561. /**
  5562. * Part of RedBeanPHP Tagging API.
  5563. * Tests whether a bean has been associated with one ore more
  5564. * of the listed tags. If the third parameter is TRUE this method
  5565. * will return TRUE only if all tags that have been specified are indeed
  5566. * associated with the given bean, otherwise FALSE.
  5567. * If the third parameter is FALSE this
  5568. * method will return TRUE if one of the tags matches, FALSE if none
  5569. * match.
  5570. *
  5571. * @param RedBean_OODBBean $bean bean to check for tags
  5572. * @param array $tags list of tags
  5573. * @param boolean $all whether they must all match or just some
  5574. *
  5575. * @return boolean $didMatch whether the bean has been assoc. with the tags
  5576. */
  5577. public function hasTag($bean, $tags, $all=false) {
  5578. $foundtags = $this->tag($bean);
  5579. if (is_string($foundtags)) $foundtags = explode(",",$tags);
  5580. $same = array_intersect($tags,$foundtags);
  5581. if ($all) {
  5582. return (implode(",",$same)===implode(",",$tags));
  5583. }
  5584. return (bool) (count($same)>0);
  5585. }
  5586. /**
  5587. * Part of RedBeanPHP Tagging API.
  5588. * Removes all sepcified tags from the bean. The tags specified in
  5589. * the second parameter will no longer be associated with the bean.
  5590. *
  5591. * @param RedBean_OODBBean $bean tagged bean
  5592. * @param array $tagList list of tags (names)
  5593. *
  5594. * @return void
  5595. */
  5596. public function untag($bean,$tagList) {
  5597. if ($tagList!==false && !is_array($tagList)) $tags = explode( ",", (string)$tagList); else $tags=$tagList;
  5598. foreach($tags as $tag) {
  5599. if ($t = $this->findTagByTitle($tag)) {
  5600. $this->associationManager->unassociate( $bean, $t );
  5601. }
  5602. }
  5603. }
  5604. /**
  5605. * Part of RedBeanPHP Tagging API.
  5606. * Tags a bean or returns tags associated with a bean.
  5607. * If $tagList is null or omitted this method will return a
  5608. * comma separated list of tags associated with the bean provided.
  5609. * If $tagList is a comma separated list (string) of tags all tags will
  5610. * be associated with the bean.
  5611. * You may also pass an array instead of a string.
  5612. *
  5613. * @param RedBean_OODBBean $bean bean
  5614. * @param mixed $tagList tags
  5615. *
  5616. * @return string $commaSepListTags
  5617. */
  5618. public function tag( RedBean_OODBBean $bean, $tagList = null ) {
  5619. if (is_null($tagList)) {
  5620. $tags = array();
  5621. $keys = $this->associationManager->related($bean, 'tag');
  5622. if ($keys) {
  5623. $tags = $this->redbean->batch('tag',$keys);
  5624. }
  5625. $foundTags = array();
  5626. foreach($tags as $tag) {
  5627. $foundTags[] = $tag->title;
  5628. }
  5629. return $foundTags;
  5630. }
  5631. $this->associationManager->clearRelations( $bean, 'tag' );
  5632. $this->addTags( $bean, $tagList );
  5633. }
  5634. /**
  5635. * Part of RedBeanPHP Tagging API.
  5636. * Adds tags to a bean.
  5637. * If $tagList is a comma separated list of tags all tags will
  5638. * be associated with the bean.
  5639. * You may also pass an array instead of a string.
  5640. *
  5641. * @param RedBean_OODBBean $bean bean
  5642. * @param array $tagList list of tags to add to bean
  5643. *
  5644. * @return void
  5645. */
  5646. public function addTags( RedBean_OODBBean $bean, $tagList ) {
  5647. if ($tagList!==false && !is_array($tagList)) $tags = explode( ",", (string)$tagList); else $tags=$tagList;
  5648. if ($tagList===false) return;
  5649. foreach($tags as $tag) {
  5650. if (!$t = $this->findTagByTitle($tag)) {
  5651. $t = $this->redbean->dispense('tag');
  5652. $t->title = $tag;
  5653. $this->redbean->store($t);
  5654. }
  5655. $this->associationManager->associate( $bean, $t );
  5656. }
  5657. }
  5658. /**
  5659. * Part of RedBeanPHP Tagging API.
  5660. * Returns all beans that have been tagged with one of the tags given.
  5661. *
  5662. * @param $beanType type of bean you are looking for
  5663. * @param $tagList list of tags to match
  5664. *
  5665. * @return array
  5666. */
  5667. public function tagged( $beanType, $tagList ) {
  5668. if ($tagList!==false && !is_array($tagList)) $tags = explode( ",", (string)$tagList); else $tags=$tagList;
  5669. $collection = array();
  5670. $tags = $this->redbean->find('tag',array('title'=>$tags));
  5671. if (count($tags)>0) {
  5672. $collectionKeys = $this->associationManager->related($tags,$beanType);
  5673. if ($collectionKeys) {
  5674. $collection = $this->redbean->batch($beanType,$collectionKeys);
  5675. }
  5676. }
  5677. return $collection;
  5678. }
  5679. /**
  5680. * Part of RedBeanPHP Tagging API.
  5681. * Returns all beans that have been tagged with ALL of the tags given.
  5682. *
  5683. * @param $beanType type of bean you are looking for
  5684. * @param $tagList list of tags to match
  5685. *
  5686. * @return array
  5687. */
  5688. public function taggedAll( $beanType, $tagList ) {
  5689. if ($tagList!==false && !is_array($tagList)) $tags = explode( ",", (string)$tagList); else $tags=$tagList;
  5690. $beans = array();
  5691. foreach($tags as $tag) {
  5692. $beans = $this->tagged($beanType,$tag);
  5693. if (isset($oldBeans)) $beans = array_intersect_assoc($beans,$oldBeans);
  5694. $oldBeans = $beans;
  5695. }
  5696. return $beans;
  5697. }
  5698. }
  5699. /**
  5700. * RedBean Facade
  5701. * @file RedBean/Facade.php
  5702. * @description Convenience class for RedBeanPHP.
  5703. * This class hides the object landscape of
  5704. * RedBeanPHP behind a single letter class providing
  5705. * almost all functionality with simple static calls.
  5706. * @author Gabor de Mooij and the RedBeanPHP Community
  5707. * @license BSD/GPLv2
  5708. *
  5709. * copyright (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community
  5710. * This source file is subject to the BSD/GPLv2 License that is bundled
  5711. * with this source code in the file license.txt.
  5712. *
  5713. */
  5714. class RedBean_Facade {
  5715. /**
  5716. * Collection of toolboxes
  5717. * @var array
  5718. */
  5719. public static $toolboxes = array();
  5720. /**
  5721. *
  5722. * Constains an instance of the RedBean Toolbox
  5723. * @var RedBean_ToolBox
  5724. *
  5725. */
  5726. public static $toolbox;
  5727. /**
  5728. * Constains an instance of RedBean OODB
  5729. * @var RedBean_OODB
  5730. */
  5731. public static $redbean;
  5732. /**
  5733. * Contains an instance of the Query Writer
  5734. * @var RedBean_QueryWriter
  5735. */
  5736. public static $writer;
  5737. /**
  5738. * Contains an instance of the Database
  5739. * Adapter.
  5740. * @var RedBean_DBAdapter
  5741. */
  5742. public static $adapter;
  5743. /**
  5744. * Contains an instance of the Association Manager
  5745. * @var RedBean_AssociationManager
  5746. */
  5747. public static $associationManager;
  5748. /**
  5749. * Contains an instance of the Extended Association Manager
  5750. * @var RedBean_ExtAssociationManager
  5751. */
  5752. public static $extAssocManager;
  5753. /**
  5754. * Holds an instance of Bean Exporter
  5755. * @var RedBean_Plugin_BeanExport
  5756. */
  5757. public static $exporter;
  5758. /**
  5759. * Holds the tag manager
  5760. * @var RedBean_TagManager
  5761. */
  5762. public static $tagManager;
  5763. /**
  5764. * Holds the Key of the current database.
  5765. * @var string
  5766. */
  5767. public static $currentDB = '';
  5768. /**
  5769. * Holds reference to SQL Helper
  5770. */
  5771. public static $f;
  5772. /**
  5773. * Get version
  5774. * @return string
  5775. */
  5776. public static function getVersion() {
  5777. return '3.2';
  5778. }
  5779. /**
  5780. * Kickstarts redbean for you. This method should be called before you start using
  5781. * RedBean. The Setup() method can be called without any arguments, in this case it will
  5782. * try to create a SQLite database in /tmp called red.db (this only works on UNIX-like systems).
  5783. *
  5784. * @param string $dsn Database connection string
  5785. * @param string $username Username for database
  5786. * @param string $password Password for database
  5787. *
  5788. * @return void
  5789. */
  5790. public static function setup( $dsn=NULL, $username=NULL, $password=NULL ) {
  5791. if (function_exists('sys_get_temp_dir')) $tmp = sys_get_temp_dir(); else $tmp = 'tmp';
  5792. if (is_null($dsn)) $dsn = 'sqlite:/'.$tmp.'/red.db';
  5793. self::addDatabase('default',$dsn,$username,$password);
  5794. self::selectDatabase('default');
  5795. return self::$toolbox;
  5796. }
  5797. /**
  5798. * Adds a database to the facade, afterwards you can select the database using
  5799. * selectDatabase($key).
  5800. *
  5801. * @param string $key ID for the database
  5802. * @param string $dsn DSN for the database
  5803. * @param string $user User for connection
  5804. * @param null|string $pass Password for connection
  5805. * @param bool $frozen Whether this database is frozen or not
  5806. *
  5807. * @return void
  5808. */
  5809. public static function addDatabase( $key, $dsn, $user=null, $pass=null, $frozen=false ) {
  5810. self::$toolboxes[$key] = RedBean_Setup::kickstart($dsn,$user,$pass,$frozen);
  5811. }
  5812. /**
  5813. * Selects a different database for the Facade to work with.
  5814. *
  5815. * @param string $key Key of the database to select
  5816. * @return int 1
  5817. */
  5818. public static function selectDatabase($key) {
  5819. if (self::$currentDB===$key) return false;
  5820. self::configureFacadeWithToolbox(self::$toolboxes[$key]);
  5821. self::$currentDB = $key;
  5822. return true;
  5823. }
  5824. /**
  5825. * Toggles DEBUG mode.
  5826. * In Debug mode all SQL that happens under the hood will
  5827. * be printed to the screen or logged by provided logger.
  5828. *
  5829. * @param boolean $tf
  5830. * @param RedBean_ILogger $logger
  5831. */
  5832. public static function debug( $tf = true, $logger = NULL ) {
  5833. if (!$logger) $logger = new RedBean_Logger;
  5834. self::$adapter->getDatabase()->setDebugMode( $tf, $logger );
  5835. }
  5836. /**
  5837. * Stores a RedBean OODB Bean and returns the ID.
  5838. *
  5839. * @param RedBean_OODBBean|RedBean_SimpleModel $bean bean
  5840. *
  5841. * @return integer $id id
  5842. */
  5843. public static function store( $bean ) {
  5844. return self::$redbean->store( $bean );
  5845. }
  5846. /**
  5847. * Toggles fluid or frozen mode. In fluid mode the database
  5848. * structure is adjusted to accomodate your objects. In frozen mode
  5849. * this is not the case.
  5850. *
  5851. * You can also pass an array containing a selection of frozen types.
  5852. * Let's call this chilly mode, it's just like fluid mode except that
  5853. * certain types (i.e. tables) aren't touched.
  5854. *
  5855. * @param boolean|array $trueFalse
  5856. */
  5857. public static function freeze( $tf = true ) {
  5858. self::$redbean->freeze( $tf );
  5859. }
  5860. /**
  5861. * Loads the bean with the given type and id and returns it.
  5862. *
  5863. * @param string $type type
  5864. * @param integer $id id of the bean you want to load
  5865. *
  5866. * @return RedBean_OODBBean $bean
  5867. */
  5868. public static function load( $type, $id ) {
  5869. return self::$redbean->load( $type, $id );
  5870. }
  5871. /**
  5872. * Deletes the specified bean.
  5873. *
  5874. * @param RedBean_OODBBean|RedBean_SimpleModel $bean bean to be deleted
  5875. *
  5876. * @return mixed
  5877. */
  5878. public static function trash( $bean ) {
  5879. return self::$redbean->trash( $bean );
  5880. }
  5881. /**
  5882. * Dispenses a new RedBean OODB Bean for use with
  5883. * the rest of the methods.
  5884. *
  5885. * @param string $type type
  5886. *
  5887. *
  5888. */
  5889. public static function dispense( $type, $num = 1 ) {
  5890. if ($num==1) {
  5891. return self::$redbean->dispense( $type );
  5892. }
  5893. else {
  5894. $beans = array();
  5895. for($v=0; $v<$num; $v++) $beans[] = self::$redbean->dispense( $type );
  5896. return $beans;
  5897. }
  5898. }
  5899. /**
  5900. * Convience method. Tries to find beans of a certain type,
  5901. * if no beans are found, it dispenses a bean of that type.
  5902. *
  5903. * @param string $type type of bean you are looking for
  5904. * @param string $sql SQL code for finding the bean
  5905. * @param array $values parameters to bind to SQL
  5906. *
  5907. * @return array $beans Contains RedBean_OODBBean instances
  5908. */
  5909. public static function findOrDispense( $type, $sql, $values ) {
  5910. $foundBeans = self::find($type,$sql,$values);
  5911. if (count($foundBeans)==0) return array(self::dispense($type)); else return $foundBeans;
  5912. }
  5913. /**
  5914. * Associates two Beans. This method will associate two beans with eachother.
  5915. * You can then get one of the beans by using the related() function and
  5916. * providing the other bean. You can also provide a base bean in the extra
  5917. * parameter. This base bean allows you to add extra information to the association
  5918. * record. Note that this is for advanced use only and the information will not
  5919. * be added to one of the beans, just to the association record.
  5920. * It's also possible to provide an array or JSON string as base bean. If you
  5921. * pass a scalar this function will interpret the base bean as having one
  5922. * property called 'extra' with the value of the scalar.
  5923. *
  5924. * @param RedBean_OODBBean $bean1 bean that will be part of the association
  5925. * @param RedBean_OODBBean $bean2 bean that will be part of the association
  5926. * @param mixed $extra bean, scalar, array or JSON providing extra data.
  5927. *
  5928. * @return mixed
  5929. */
  5930. public static function associate( RedBean_OODBBean $bean1, RedBean_OODBBean $bean2, $extra = null ) {
  5931. //No extra? Just associate like always (default)
  5932. if (!$extra) {
  5933. return self::$associationManager->associate( $bean1, $bean2 );
  5934. }
  5935. else{
  5936. if (!is_array($extra)) {
  5937. $info = json_decode($extra,true);
  5938. if (!$info) $info = array('extra'=>$extra);
  5939. }
  5940. else {
  5941. $info = $extra;
  5942. }
  5943. $bean = RedBean_Facade::dispense('typeLess');
  5944. $bean->import($info);
  5945. return self::$extAssocManager->extAssociate($bean1, $bean2, $bean);
  5946. }
  5947. }
  5948. /**
  5949. * Breaks the association between two beans.
  5950. * This functions breaks the association between a pair of beans. After
  5951. * calling this functions the beans will no longer be associated with
  5952. * eachother. Calling related() with either one of the beans will no longer
  5953. * return the other bean.
  5954. *
  5955. * @param RedBean_OODBBean $bean1 bean
  5956. * @param RedBean_OODBBean $bean2 bean
  5957. *
  5958. * @return mixed
  5959. */
  5960. public static function unassociate( RedBean_OODBBean $bean1, RedBean_OODBBean $bean2 , $fast=false) {
  5961. return self::$associationManager->unassociate( $bean1, $bean2, $fast );
  5962. }
  5963. /**
  5964. * Returns all the beans associated with $bean.
  5965. * This method will return an array containing all the beans that have
  5966. * been associated once with the associate() function and are still
  5967. * associated with the bean specified. The type parameter indicates the
  5968. * type of beans you are looking for. You can also pass some extra SQL and
  5969. * values for that SQL to filter your results after fetching the
  5970. * related beans.
  5971. *
  5972. * Dont try to make use of subqueries, a subquery using IN() seems to
  5973. * be slower than two queries!
  5974. *
  5975. * Since 3.2, you can now also pass an array of beans instead just one
  5976. * bean as the first parameter.
  5977. *
  5978. * @param RedBean_OODBBean|array $bean the bean you have
  5979. * @param string $type the type of beans you want
  5980. * @param string $sql SQL snippet for extra filtering
  5981. * @param array $val values to be inserted in SQL slots
  5982. *
  5983. * @return array $beans beans yielded by your query.
  5984. */
  5985. public static function related( $bean, $type, $sql=null, $values=array()) {
  5986. $keys = self::$associationManager->related( $bean, $type );
  5987. if (count($keys)==0) return array();
  5988. if (!$sql) return self::batch($type, $keys);
  5989. $rows = self::$writer->selectRecord( $type, array('id'=>$keys),array($sql,$values),false );
  5990. return self::$redbean->convertToBeans($type,$rows);
  5991. }
  5992. /**
  5993. * Returns only single associated bean.
  5994. *
  5995. * @param RedBean_OODBBean $bean bean provided
  5996. * @param string $type type of bean you are searching for
  5997. * @param string $sql SQL for extra filtering
  5998. * @param array $values values to be inserted in SQL slots
  5999. *
  6000. *
  6001. * @return RedBean_OODBBean $bean
  6002. */
  6003. public static function relatedOne( RedBean_OODBBean $bean, $type, $sql=null, $values=array() ) {
  6004. $beans = self::related($bean, $type, $sql, $values);
  6005. if (count($beans)==0) return null;
  6006. return reset( $beans );
  6007. }
  6008. /**
  6009. * Checks whether a pair of beans is related N-M. This function does not
  6010. * check whether the beans are related in N:1 way.
  6011. *
  6012. * @param RedBean_OODBBean $bean1 first bean
  6013. * @param RedBean_OODBBean $bean2 second bean
  6014. *
  6015. * @return bool $yesNo whether they are related
  6016. */
  6017. public static function areRelated( RedBean_OODBBean $bean1, RedBean_OODBBean $bean2) {
  6018. return self::$associationManager->areRelated($bean1,$bean2);
  6019. }
  6020. /**
  6021. * The opposite of related(). Returns all the beans that are not
  6022. * associated with the bean provided.
  6023. *
  6024. * @param RedBean_OODBBean $bean bean provided
  6025. * @param string $type type of bean you are searching for
  6026. * @param string $sql SQL for extra filtering
  6027. * @param array $values values to be inserted in SQL slots
  6028. *
  6029. * @return array $beans beans
  6030. */
  6031. public static function unrelated(RedBean_OODBBean $bean, $type, $sql=null, $values=array()) {
  6032. $keys = self::$associationManager->related( $bean, $type );
  6033. $rows = self::$writer->selectRecord( $type, array('id'=>$keys), array($sql,$values), false, true );
  6034. return self::$redbean->convertToBeans($type,$rows);
  6035. }
  6036. /**
  6037. * Clears all associated beans.
  6038. * Breaks all many-to-many associations of a bean and a specified type.
  6039. *
  6040. * @param RedBean_OODBBean $bean bean you wish to clear many-to-many relations for
  6041. * @param string $type type of bean you wish to break associatons with
  6042. *
  6043. * @return void
  6044. */
  6045. public static function clearRelations( RedBean_OODBBean $bean, $type ) {
  6046. self::$associationManager->clearRelations( $bean, $type );
  6047. }
  6048. /**
  6049. * Finds a bean using a type and a where clause (SQL).
  6050. * As with most Query tools in RedBean you can provide values to
  6051. * be inserted in the SQL statement by populating the value
  6052. * array parameter; you can either use the question mark notation
  6053. * or the slot-notation (:keyname).
  6054. *
  6055. * @param string $type type the type of bean you are looking for
  6056. * @param string $sql sql SQL query to find the desired bean, starting right after WHERE clause
  6057. * @param array $values values array of values to be bound to parameters in query
  6058. *
  6059. * @return array $beans beans
  6060. */
  6061. public static function find( $type, $sql=null, $values=array() ) {
  6062. if ($sql instanceof RedBean_SQLHelper) list($sql,$values) = $sql->getQuery();
  6063. if (!is_array($values)) throw new InvalidArgumentException('Expected array, ' . gettype($values) . ' given.');
  6064. return self::$redbean->find($type,array(),array($sql,$values));
  6065. }
  6066. /**
  6067. * Finds a bean using a type and a where clause (SQL).
  6068. * As with most Query tools in RedBean you can provide values to
  6069. * be inserted in the SQL statement by populating the value
  6070. * array parameter; you can either use the question mark notation
  6071. * or the slot-notation (:keyname).
  6072. * The findAll() method differs from the find() method in that it does
  6073. * not assume a WHERE-clause, so this is valid:
  6074. *
  6075. * R::findAll('person',' ORDER BY name DESC ');
  6076. *
  6077. * Your SQL does not have to start with a valid WHERE-clause condition.
  6078. *
  6079. * @param string $type type the type of bean you are looking for
  6080. * @param string $sql sql SQL query to find the desired bean, starting right after WHERE clause
  6081. * @param array $values values array of values to be bound to parameters in query
  6082. *
  6083. * @return array $beans beans
  6084. */
  6085. public static function findAll( $type, $sql=null, $values=array() ) {
  6086. if (!is_array($values)) throw new InvalidArgumentException('Expected array, ' . gettype($values) . ' given.');
  6087. return self::$redbean->find($type,array(),array($sql,$values),true);
  6088. }
  6089. /**
  6090. * Finds a bean using a type and a where clause (SQL).
  6091. * As with most Query tools in RedBean you can provide values to
  6092. * be inserted in the SQL statement by populating the value
  6093. * array parameter; you can either use the question mark notation
  6094. * or the slot-notation (:keyname).
  6095. * The variation also exports the beans (i.e. it returns arrays).
  6096. *
  6097. * @param string $type type the type of bean you are looking for
  6098. * @param string $sql sql SQL query to find the desired bean, starting right after WHERE clause
  6099. * @param array $values values array of values to be bound to parameters in query
  6100. *
  6101. * @return array $arrays arrays
  6102. */
  6103. public static function findAndExport($type, $sql=null, $values=array()) {
  6104. $items = self::find( $type, $sql, $values );
  6105. $arr = array();
  6106. foreach($items as $key=>$item) {
  6107. $arr[$key]=$item->export();
  6108. }
  6109. return $arr;
  6110. }
  6111. /**
  6112. * Finds a bean using a type and a where clause (SQL).
  6113. * As with most Query tools in RedBean you can provide values to
  6114. * be inserted in the SQL statement by populating the value
  6115. * array parameter; you can either use the question mark notation
  6116. * or the slot-notation (:keyname).
  6117. * This variation returns the first bean only.
  6118. *
  6119. * @param string $type type the type of bean you are looking for
  6120. * @param string $sql sql SQL query to find the desired bean, starting right after WHERE clause
  6121. * @param array $values values array of values to be bound to parameters in query
  6122. *
  6123. * @return RedBean_OODBBean $bean
  6124. */
  6125. public static function findOne( $type, $sql=null, $values=array()) {
  6126. $items = self::find($type,$sql,$values);
  6127. $found = reset($items);
  6128. if (!$found) return null;
  6129. return $found;
  6130. }
  6131. /**
  6132. * Finds a bean using a type and a where clause (SQL).
  6133. * As with most Query tools in RedBean you can provide values to
  6134. * be inserted in the SQL statement by populating the value
  6135. * array parameter; you can either use the question mark notation
  6136. * or the slot-notation (:keyname).
  6137. * This variation returns the last bean only.
  6138. *
  6139. * @param string $type type the type of bean you are looking for
  6140. * @param string $sql sql SQL query to find the desired bean, starting right after WHERE clause
  6141. * @param array $values values array of values to be bound to parameters in query
  6142. *
  6143. * @return RedBean_OODBBean $bean
  6144. */
  6145. public static function findLast( $type, $sql=null, $values=array() ) {
  6146. $items = self::find( $type, $sql, $values );
  6147. $found = end( $items );
  6148. if (!$found) return null;
  6149. return $found;
  6150. }
  6151. /**
  6152. * Returns an array of beans. Pass a type and a series of ids and
  6153. * this method will bring you the correspondig beans.
  6154. *
  6155. * important note: Because this method loads beans using the load()
  6156. * function (but faster) it will return empty beans with ID 0 for
  6157. * every bean that could not be located. The resulting beans will have the
  6158. * passed IDs as their keys.
  6159. *
  6160. * @param string $type type of beans
  6161. * @param array $ids ids to load
  6162. *
  6163. * @return array $beans resulting beans (may include empty ones)
  6164. */
  6165. public static function batch( $type, $ids ) {
  6166. return self::$redbean->batch($type, $ids);
  6167. }
  6168. /**
  6169. * Convenience function to execute Queries directly.
  6170. * Executes SQL.
  6171. *
  6172. * @param string $sql sql SQL query to execute
  6173. * @param array $values values a list of values to be bound to query parameters
  6174. *
  6175. * @return integer $affected number of affected rows
  6176. */
  6177. public static function exec( $sql, $values=array() ) {
  6178. return self::query('exec',$sql,$values);
  6179. }
  6180. /**
  6181. * Convenience function to execute Queries directly.
  6182. * Executes SQL.
  6183. *
  6184. * @param string $sql sql SQL query to execute
  6185. * @param array $values values a list of values to be bound to query parameters
  6186. *
  6187. * @return array $results
  6188. */
  6189. public static function getAll( $sql, $values=array() ) {
  6190. return self::query('get',$sql,$values);
  6191. }
  6192. /**
  6193. * Convenience function to execute Queries directly.
  6194. * Executes SQL.
  6195. *
  6196. * @param string $sql sql SQL query to execute
  6197. * @param array $values values a list of values to be bound to query parameters
  6198. *
  6199. * @return string $result scalar
  6200. */
  6201. public static function getCell( $sql, $values=array() ) {
  6202. return self::query('getCell',$sql,$values);
  6203. }
  6204. /**
  6205. * Convenience function to execute Queries directly.
  6206. * Executes SQL.
  6207. *
  6208. * @param string $sql sql SQL query to execute
  6209. * @param array $values values a list of values to be bound to query parameters
  6210. *
  6211. * @return array $results
  6212. */
  6213. public static function getRow( $sql, $values=array() ) {
  6214. return self::query('getRow',$sql,$values);
  6215. }
  6216. /**
  6217. * Convenience function to execute Queries directly.
  6218. * Executes SQL.
  6219. *
  6220. * @param string $sql sql SQL query to execute
  6221. * @param array $values values a list of values to be bound to query parameters
  6222. *
  6223. * @return array $results
  6224. */
  6225. public static function getCol( $sql, $values=array() ) {
  6226. return self::query('getCol',$sql,$values);
  6227. }
  6228. /**
  6229. * Internal Query function, executes the desired query. Used by
  6230. * all facade query functions. This keeps things DRY.
  6231. *
  6232. * @throws RedBean_Exception_SQL
  6233. *
  6234. * @param string $method desired query method (i.e. 'cell','col','exec' etc..)
  6235. * @param string $sql the sql you want to execute
  6236. * @param array $values array of values to be bound to query statement
  6237. *
  6238. * @return array $results results of query
  6239. */
  6240. private static function query($method,$sql,$values) {
  6241. if (!self::$redbean->isFrozen()) {
  6242. try {
  6243. $rs = RedBean_Facade::$adapter->$method( $sql, $values );
  6244. }catch(RedBean_Exception_SQL $e) {
  6245. if(self::$writer->sqlStateIn($e->getSQLState(),
  6246. array(
  6247. RedBean_QueryWriter::C_SQLSTATE_NO_SUCH_COLUMN,
  6248. RedBean_QueryWriter::C_SQLSTATE_NO_SUCH_TABLE)
  6249. )) {
  6250. return array();
  6251. }
  6252. else {
  6253. throw $e;
  6254. }
  6255. }
  6256. return $rs;
  6257. }
  6258. else {
  6259. return RedBean_Facade::$adapter->$method( $sql, $values );
  6260. }
  6261. }
  6262. /**
  6263. * Convenience function to execute Queries directly.
  6264. * Executes SQL.
  6265. * Results will be returned as an associative array. The first
  6266. * column in the select clause will be used for the keys in this array and
  6267. * the second column will be used for the values. If only one column is
  6268. * selected in the query, both key and value of the array will have the
  6269. * value of this field for each row.
  6270. *
  6271. * @param string $sql sql SQL query to execute
  6272. * @param array $values values a list of values to be bound to query parameters
  6273. *
  6274. * @return array $results
  6275. */
  6276. public static function getAssoc($sql,$values=array()) {
  6277. return self::query('getAssoc',$sql,$values);
  6278. }
  6279. /**
  6280. * Makes a copy of a bean. This method makes a deep copy
  6281. * of the bean.The copy will have the following features.
  6282. * - All beans in own-lists will be duplicated as well
  6283. * - All references to shared beans will be copied but not the shared beans themselves
  6284. * - All references to parent objects (_id fields) will be copied but not the parents themselves
  6285. * In most cases this is the desired scenario for copying beans.
  6286. * This function uses a trail-array to prevent infinite recursion, if a recursive bean is found
  6287. * (i.e. one that already has been processed) the ID of the bean will be returned.
  6288. * This should not happen though.
  6289. *
  6290. * Note:
  6291. * This function does a reflectional database query so it may be slow.
  6292. *
  6293. * @param RedBean_OODBBean $bean bean to be copied
  6294. * @param array $trail for internal usage, pass array()
  6295. * @param boolean $pid for internal usage
  6296. *
  6297. * @return array $copiedBean the duplicated bean
  6298. */
  6299. public static function dup($bean,$trail=array(),$pid=false) {
  6300. $duplicationManager = new RedBean_DuplicationManager(self::$toolbox);
  6301. return $duplicationManager->dup($bean, $trail,$pid);
  6302. }
  6303. /**
  6304. * Exports a collection of beans. Handy for XML/JSON exports with a
  6305. * Javascript framework like Dojo or ExtJS.
  6306. * What will be exported:
  6307. * - contents of the bean
  6308. * - all own bean lists (recursively)
  6309. * - all shared beans (not THEIR own lists)
  6310. *
  6311. * @param array|RedBean_OODBBean $beans beans to be exported
  6312. *
  6313. * @return array $array exported structure
  6314. */
  6315. public static function exportAll($beans) {
  6316. $array = array();
  6317. if (!is_array($beans)) $beans = array($beans);
  6318. foreach($beans as $bean) {
  6319. $f = self::dup($bean,array(),true);
  6320. $array[] = $f->export();
  6321. }
  6322. return $array;
  6323. }
  6324. /**
  6325. * Given an array of two beans and a property, this method
  6326. * swaps the value of the property.
  6327. * This is handy if you need to swap the priority or orderNo
  6328. * of an item (i.e. bug-tracking, page order).
  6329. *
  6330. * @param array $beans beans
  6331. * @param string $property property
  6332. */
  6333. public static function swap( $beans, $property ) {
  6334. $bean1 = array_shift($beans);
  6335. $bean2 = array_shift($beans);
  6336. $tmp = $bean1->$property;
  6337. $bean1->$property = $bean2->$property;
  6338. $bean2->$property = $tmp;
  6339. RedBean_Facade::store($bean1);
  6340. RedBean_Facade::store($bean2);
  6341. }
  6342. /**
  6343. * Converts a series of rows to beans.
  6344. *
  6345. * @param string $type type
  6346. * @param array $rows must contain an array of arrays.
  6347. *
  6348. * @return array $beans
  6349. */
  6350. public static function convertToBeans($type,$rows) {
  6351. return self::$redbean->convertToBeans($type,$rows);
  6352. }
  6353. /**
  6354. * Part of RedBeanPHP Tagging API.
  6355. * Tests whether a bean has been associated with one ore more
  6356. * of the listed tags. If the third parameter is TRUE this method
  6357. * will return TRUE only if all tags that have been specified are indeed
  6358. * associated with the given bean, otherwise FALSE.
  6359. * If the third parameter is FALSE this
  6360. * method will return TRUE if one of the tags matches, FALSE if none
  6361. * match.
  6362. *
  6363. * @param RedBean_OODBBean $bean bean to check for tags
  6364. * @param array $tags list of tags
  6365. * @param boolean $all whether they must all match or just some
  6366. *
  6367. * @return boolean $didMatch whether the bean has been assoc. with the tags
  6368. */
  6369. public static function hasTag($bean, $tags, $all=false) {
  6370. return self::$tagManager->hasTag($bean,$tags,$all);
  6371. }
  6372. /**
  6373. * Part of RedBeanPHP Tagging API.
  6374. * Removes all sepcified tags from the bean. The tags specified in
  6375. * the second parameter will no longer be associated with the bean.
  6376. *
  6377. * @param RedBean_OODBBean $bean tagged bean
  6378. * @param array $tagList list of tags (names)
  6379. *
  6380. * @return void
  6381. */
  6382. public static function untag($bean,$tagList) {
  6383. return self::$tagManager->untag($bean,$tagList);
  6384. }
  6385. /**
  6386. * Part of RedBeanPHP Tagging API.
  6387. * Tags a bean or returns tags associated with a bean.
  6388. * If $tagList is null or omitted this method will return a
  6389. * comma separated list of tags associated with the bean provided.
  6390. * If $tagList is a comma separated list (string) of tags all tags will
  6391. * be associated with the bean.
  6392. * You may also pass an array instead of a string.
  6393. *
  6394. * @param RedBean_OODBBean $bean bean
  6395. * @param mixed $tagList tags
  6396. *
  6397. * @return string $commaSepListTags
  6398. */
  6399. public static function tag( RedBean_OODBBean $bean, $tagList = null ) {
  6400. return self::$tagManager->tag($bean,$tagList);
  6401. }
  6402. /**
  6403. * Part of RedBeanPHP Tagging API.
  6404. * Adds tags to a bean.
  6405. * If $tagList is a comma separated list of tags all tags will
  6406. * be associated with the bean.
  6407. * You may also pass an array instead of a string.
  6408. *
  6409. * @param RedBean_OODBBean $bean bean
  6410. * @param array $tagList list of tags to add to bean
  6411. *
  6412. * @return void
  6413. */
  6414. public static function addTags( RedBean_OODBBean $bean, $tagList ) {
  6415. return self::$tagManager->addTags($bean,$tagList);
  6416. }
  6417. /**
  6418. * Part of RedBeanPHP Tagging API.
  6419. * Returns all beans that have been tagged with one of the tags given.
  6420. *
  6421. * @param $beanType type of bean you are looking for
  6422. * @param $tagList list of tags to match
  6423. *
  6424. * @return array
  6425. */
  6426. public static function tagged( $beanType, $tagList ) {
  6427. return self::$tagManager->tagged($beanType,$tagList);
  6428. }
  6429. /**
  6430. * Part of RedBeanPHP Tagging API.
  6431. * Returns all beans that have been tagged with ALL of the tags given.
  6432. *
  6433. * @param $beanType type of bean you are looking for
  6434. * @param $tagList list of tags to match
  6435. *
  6436. * @return array
  6437. */
  6438. public static function taggedAll( $beanType, $tagList ) {
  6439. return self::$tagManager->taggedAll($beanType,$tagList);
  6440. }
  6441. /**
  6442. * Wipes all beans of type $beanType.
  6443. *
  6444. * @param string $beanType type of bean you want to destroy entirely.
  6445. */
  6446. public static function wipe( $beanType ) {
  6447. return RedBean_Facade::$redbean->wipe($beanType);
  6448. }
  6449. /**
  6450. * Counts beans
  6451. *
  6452. * @param string $beanType type of bean
  6453. *
  6454. * @return integer $numOfBeans
  6455. */
  6456. public static function count( $beanType ) {
  6457. return RedBean_Facade::$redbean->count($beanType);
  6458. }
  6459. /**
  6460. * Configures the facade, want to have a new Writer? A new Object Database or a new
  6461. * Adapter and you want it on-the-fly? Use this method to hot-swap your facade with a new
  6462. * toolbox.
  6463. *
  6464. * @param RedBean_ToolBox $tb toolbox
  6465. *
  6466. * @return RedBean_ToolBox $tb old, rusty, previously used toolbox
  6467. */
  6468. public static function configureFacadeWithToolbox( RedBean_ToolBox $tb ) {
  6469. $oldTools = self::$toolbox;
  6470. self::$toolbox = $tb;
  6471. self::$writer = self::$toolbox->getWriter();
  6472. self::$adapter = self::$toolbox->getDatabaseAdapter();
  6473. self::$redbean = self::$toolbox->getRedBean();
  6474. self::$associationManager = new RedBean_AssociationManager( self::$toolbox );
  6475. self::$redbean->setAssociationManager(self::$associationManager);
  6476. self::$extAssocManager = new RedBean_ExtAssociationManager( self::$toolbox );
  6477. $helper = new RedBean_ModelHelper();
  6478. self::$redbean->addEventListener('update', $helper );
  6479. self::$redbean->addEventListener('open', $helper );
  6480. self::$redbean->addEventListener('delete', $helper );
  6481. self::$associationManager->addEventListener('delete', $helper );
  6482. self::$redbean->addEventListener('after_delete', $helper );
  6483. self::$redbean->addEventListener('after_update', $helper );
  6484. self::$redbean->addEventListener('dispense', $helper );
  6485. self::$tagManager = new RedBean_TagManager( self::$toolbox );
  6486. self::$f = new RedBean_SQLHelper(self::$adapter);
  6487. return $oldTools;
  6488. }
  6489. /**
  6490. * facade method for Cooker Graph.
  6491. *
  6492. * @param array $array array containing POST/GET fields or other data
  6493. * @param boolean $filterEmptyBeans whether you want to exclude empty beans
  6494. *
  6495. * @return array $arrayOfBeans Beans
  6496. */
  6497. public static function graph($array,$filterEmpty=false) {
  6498. $cooker = new RedBean_Cooker();
  6499. $cooker->setToolbox(self::$toolbox);
  6500. return $cooker->graph($array,$filterEmpty);
  6501. }
  6502. /**
  6503. * Facade Convience method for adapter transaction system.
  6504. * Begins a transaction.
  6505. *
  6506. * @return void
  6507. */
  6508. public static function begin() {
  6509. self::$adapter->startTransaction();
  6510. }
  6511. /**
  6512. * Facade Convience method for adapter transaction system.
  6513. * Commits a transaction.
  6514. *
  6515. * @return void
  6516. */
  6517. public static function commit() {
  6518. self::$adapter->commit();
  6519. }
  6520. /**
  6521. * Facade Convience method for adapter transaction system.
  6522. * Rolls back a transaction.
  6523. *
  6524. * @return void
  6525. */
  6526. public static function rollback() {
  6527. self::$adapter->rollback();
  6528. }
  6529. /**
  6530. * Returns a list of columns. Format of this array:
  6531. * array( fieldname => type )
  6532. * Note that this method only works in fluid mode because it might be
  6533. * quite heavy on production servers!
  6534. *
  6535. * @param string $table name of the table (not type) you want to get columns of
  6536. *
  6537. * @return array $columns list of columns and their types
  6538. */
  6539. public static function getColumns($table) {
  6540. return self::$writer->getColumns($table);
  6541. }
  6542. /**
  6543. * Generates question mark slots for an array of values.
  6544. *
  6545. * @param array $array
  6546. * @return string $slots
  6547. */
  6548. public static function genSlots($array) {
  6549. if (count($array)>0) {
  6550. $filler = array_fill(0,count($array),'?');
  6551. return implode(',',$filler);
  6552. }
  6553. else {
  6554. return '';
  6555. }
  6556. }
  6557. /**
  6558. * Nukes the entire database.
  6559. */
  6560. public static function nuke() {
  6561. if (!self::$redbean->isFrozen()) {
  6562. self::$writer->wipeAll();
  6563. }
  6564. }
  6565. /**
  6566. * Sets a list of dependencies.
  6567. * A dependency list contains an entry for each dependent bean.
  6568. * A dependent bean will be removed if the relation with one of the
  6569. * dependencies gets broken.
  6570. *
  6571. * Example:
  6572. *
  6573. * array(
  6574. * 'page' => array('book','magazine')
  6575. * )
  6576. *
  6577. * A page will be removed if:
  6578. *
  6579. * unset($book->ownPage[$pageID]);
  6580. *
  6581. * or:
  6582. *
  6583. * unset($magazine->ownPage[$pageID]);
  6584. *
  6585. * but not if:
  6586. *
  6587. * unset($paper->ownPage[$pageID]);
  6588. *
  6589. *
  6590. * @param array $dep list of dependencies
  6591. */
  6592. public static function dependencies($dep) {
  6593. self::$redbean->setDepList($dep);
  6594. }
  6595. /**
  6596. * Short hand function to store a set of beans at once, IDs will be
  6597. * returned as an array. For information please consult the R::store()
  6598. * function.
  6599. * A loop saver.
  6600. *
  6601. * @param array $beans list of beans to be stored
  6602. *
  6603. * @return array $ids list of resulting IDs
  6604. */
  6605. public static function storeAll($beans) {
  6606. $ids = array();
  6607. foreach($beans as $bean) $ids[] = self::store($bean);
  6608. return $ids;
  6609. }
  6610. /**
  6611. * Short hand function to trash a set of beans at once.
  6612. * For information please consult the R::trash() function.
  6613. * A loop saver.
  6614. *
  6615. * @param array $beans list of beans to be trashed
  6616. */
  6617. public static function trashAll($beans) {
  6618. foreach($beans as $bean) self::trash($bean);
  6619. }
  6620. /**
  6621. * A label is a bean with only an id, type and name property.
  6622. * This function will dispense beans for all entries in the array. The
  6623. * values of the array will be assigned to the name property of each
  6624. * individual bean.
  6625. *
  6626. * @param string $type type of beans you would like to have
  6627. * @param array $labels list of labels, names for each bean
  6628. *
  6629. * @return array $bean a list of beans with type and name property
  6630. */
  6631. public static function dispenseLabels($type,$labels) {
  6632. $labelBeans = array();
  6633. foreach($labels as $label) {
  6634. $labelBean = self::dispense($type);
  6635. $labelBean->name = $label;
  6636. $labelBeans[] = $labelBean;
  6637. }
  6638. return $labelBeans;
  6639. }
  6640. /**
  6641. * Gathers labels from beans. This function loops through the beans,
  6642. * collects the values of the name properties of each individual bean
  6643. * and stores the names in a new array. The array then gets sorted using the
  6644. * default sort function of PHP (sort).
  6645. *
  6646. * @param array $beans list of beans to loop
  6647. *
  6648. * @return array $array list of names of beans
  6649. */
  6650. public function gatherLabels($beans) {
  6651. $labels = array();
  6652. foreach($beans as $bean) $labels[] = $bean->name;
  6653. sort($labels);
  6654. return $labels;
  6655. }
  6656. /**
  6657. * Closes the database connection.
  6658. */
  6659. public static function close() {
  6660. if (isset(self::$adapter)){
  6661. self::$adapter->close();
  6662. }
  6663. }
  6664. /**
  6665. * Activates TimeLine Schema Alteration monitoring and
  6666. * Query logging.
  6667. *
  6668. * @param type $filename
  6669. */
  6670. public static function log($filename) {
  6671. $tl = new RedBean_Plugin_TimeLine($filename);
  6672. self::$adapter->addEventListener('sql_exec',$tl);
  6673. }
  6674. /**
  6675. * Simple convenience function, returns ISO date formatted representation
  6676. * of $time.
  6677. *
  6678. * @param mixed $time UNIX timestamp
  6679. *
  6680. * @return type
  6681. */
  6682. public static function isoDate( $time = null ) {
  6683. if (!$time) $time = time();
  6684. return @date('Y-m-d',$time);
  6685. }
  6686. /**
  6687. * Simple convenience function, returns ISO date time
  6688. * formatted representation
  6689. * of $time.
  6690. *
  6691. * @param mixed $time UNIX timestamp
  6692. *
  6693. * @return type
  6694. */
  6695. public static function isoDateTime( $time = null) {
  6696. if (!$time) $time = time();
  6697. return @date('Y-m-d H:i:s',$time);
  6698. }
  6699. }
  6700. //Compatibility with PHP 5.2 and earlier
  6701. function __lcfirst( $str ){ return (string)(strtolower(substr($str,0,1)).substr($str,1)); }
  6702. /**
  6703. * BeanCan
  6704. *
  6705. * @file RedBean/BeanCan.php
  6706. * @description A Server Interface for RedBean and Fuse.
  6707. * @author Gabor de Mooij and the RedBeanPHP Community
  6708. * @license BSD/GPLv2
  6709. *
  6710. * The BeanCan Server is a lightweight, minimalistic server interface for
  6711. * RedBean that can perfectly act as an ORM middleware solution or a backend
  6712. * for an AJAX application.
  6713. *
  6714. * (c) copyright G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community.
  6715. * This source file is subject to the BSD/GPLv2 License that is bundled
  6716. * with this source code in the file license.txt.
  6717. */
  6718. class RedBean_BeanCan {
  6719. /**
  6720. * Holds a FUSE instance.
  6721. * @var RedBean_ModelHelper
  6722. */
  6723. private $modelHelper;
  6724. /**
  6725. * Constructor.
  6726. */
  6727. public function __construct() {
  6728. $this->modelHelper = new RedBean_ModelHelper;
  6729. }
  6730. /**
  6731. * Writes a response object for the client (JSON encoded). Internal method.
  6732. *
  6733. * @param mixed $result result
  6734. * @param integer $id request ID
  6735. * @param integer $errorCode error code from server
  6736. * @param string $errorMessage error message from server
  6737. *
  6738. * @return string $json JSON encoded response.
  6739. */
  6740. private function resp($result=null, $id=null, $errorCode='-32603',$errorMessage='Internal Error') {
  6741. $response = array('jsonrpc'=>'2.0');
  6742. if ($id) { $response['id'] = $id; }
  6743. if ($result) {
  6744. $response['result']=$result;
  6745. }
  6746. else {
  6747. $response['error'] = array('code'=>$errorCode,'message'=>$errorMessage);
  6748. }
  6749. return (json_encode($response));
  6750. }
  6751. /**
  6752. * Processes a JSON object request.
  6753. *
  6754. * @param array $jsonObject JSON request object
  6755. *
  6756. * @return mixed $result result
  6757. */
  6758. public function handleJSONRequest( $jsonString ) {
  6759. //Decode JSON string
  6760. $jsonArray = json_decode($jsonString,true);
  6761. if (!$jsonArray) return $this->resp(null,null,-32700,'Cannot Parse JSON');
  6762. if (!isset($jsonArray['jsonrpc'])) return $this->resp(null,null,-32600,'No RPC version');
  6763. if (($jsonArray['jsonrpc']!='2.0')) return $this->resp(null,null,-32600,'Incompatible RPC Version');
  6764. //DO we have an ID to identify this request?
  6765. if (!isset($jsonArray['id'])) return $this->resp(null,null,-32600,'No ID');
  6766. //Fetch the request Identification String.
  6767. $id = $jsonArray['id'];
  6768. //Do we have a method?
  6769. if (!isset($jsonArray['method'])) return $this->resp(null,$id,-32600,'No method');
  6770. //Do we have params?
  6771. if (!isset($jsonArray['params'])) {
  6772. $data = array();
  6773. }
  6774. else {
  6775. $data = $jsonArray['params'];
  6776. }
  6777. //Check method signature
  6778. $method = explode(':',trim($jsonArray['method']));
  6779. if (count($method)!=2) {
  6780. return $this->resp(null, $id, -32600,'Invalid method signature. Use: BEAN:ACTION');
  6781. }
  6782. //Collect Bean and Action
  6783. $beanType = $method[0];
  6784. $action = $method[1];
  6785. //May not contain anything other than ALPHA NUMERIC chars and _
  6786. if (preg_match('/\W/',$beanType)) return $this->resp(null, $id, -32600,'Invalid Bean Type String');
  6787. if (preg_match('/\W/',$action)) return $this->resp(null, $id, -32600,'Invalid Action String');
  6788. try {
  6789. switch($action) {
  6790. case 'store':
  6791. if (!isset($data[0])) return $this->resp(null, $id, -32602,'First param needs to be Bean Object');
  6792. $data = $data[0];
  6793. if (!isset($data['id'])) $bean = RedBean_Facade::dispense($beanType); else
  6794. $bean = RedBean_Facade::load($beanType,$data['id']);
  6795. $bean->import( $data );
  6796. $rid = RedBean_Facade::store($bean);
  6797. return $this->resp($rid, $id);
  6798. case 'load':
  6799. if (!isset($data[0])) return $this->resp(null, $id, -32602,'First param needs to be Bean ID');
  6800. $bean = RedBean_Facade::load($beanType,$data[0]);
  6801. return $this->resp($bean->export(),$id);
  6802. case 'trash':
  6803. if (!isset($data[0])) return $this->resp(null, $id, -32602,'First param needs to be Bean ID');
  6804. $bean = RedBean_Facade::load($beanType,$data[0]);
  6805. RedBean_Facade::trash($bean);
  6806. return $this->resp('OK',$id);
  6807. default:
  6808. $modelName = $this->modelHelper->getModelName( $beanType );
  6809. if (!class_exists($modelName)) return $this->resp(null, $id, -32601,'No such bean in the can!');
  6810. $beanModel = new $modelName;
  6811. if (!method_exists($beanModel,$action)) return $this->resp(null, $id, -32601,"Method not found in Bean: $beanType ");
  6812. return $this->resp( call_user_func_array(array($beanModel,$action), $data), $id);
  6813. }
  6814. }
  6815. catch(Exception $exception) {
  6816. return $this->resp(null, $id, -32099,$exception->getCode().'-'.$exception->getMessage());
  6817. }
  6818. }
  6819. /**
  6820. * Support for RESTFul GET-requests.
  6821. * Only supports very BASIC REST requests, for more functionality please use
  6822. * the JSON-RPC 2 interface.
  6823. *
  6824. * @param string $pathToResource RESTFul path to resource
  6825. *
  6826. * @return string $json a JSON encoded response ready for sending to client
  6827. */
  6828. public function handleRESTGetRequest( $pathToResource ) {
  6829. if (!is_string($pathToResource)) return $this->resp(null,0,-32099,'IR');
  6830. $resourceInfo = explode('/',$pathToResource);
  6831. $type = $resourceInfo[0];
  6832. try {
  6833. if (count($resourceInfo) < 2) {
  6834. return $this->resp(RedBean_Facade::findAndExport($type));
  6835. }
  6836. else {
  6837. $id = (int) $resourceInfo[1];
  6838. return $this->resp(RedBean_Facade::load($type,$id)->export(),$id);
  6839. }
  6840. }
  6841. catch(Exception $e) {
  6842. return $this->resp(null,0,-32099);
  6843. }
  6844. }
  6845. }
  6846. /**
  6847. * RedBean Cooker
  6848. * @file RedBean/Cooker.php
  6849. * @description Turns arrays into bean collections for easy persistence.
  6850. * @author Gabor de Mooij and the RedBeanPHP Community
  6851. * @license BSD/GPLv2
  6852. *
  6853. * The Cooker is a little candy to make it easier to read-in an HTML form.
  6854. * This class turns a form into a collection of beans plus an array
  6855. * describing the desired associations.
  6856. *
  6857. * (c) copyright G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community.
  6858. * This source file is subject to the BSD/GPLv2 License that is bundled
  6859. * with this source code in the file license.txt.
  6860. */
  6861. class RedBean_Cooker {
  6862. /**
  6863. * This flag indicates whether empty strings in beans will be
  6864. * interpreted as NULL or not. TRUE means Yes, will be converted to NULL,
  6865. * FALSE means empty strings will be stored as such (conversion to 0 for integer fields).
  6866. * @var boolean
  6867. */
  6868. private static $useNULLForEmptyString = false;
  6869. /**
  6870. * Sets the toolbox to be used by graph()
  6871. *
  6872. * @param RedBean_Toolbox $toolbox toolbox
  6873. * @return void
  6874. */
  6875. public function setToolbox(RedBean_Toolbox $toolbox) {
  6876. $this->toolbox = $toolbox;
  6877. $this->redbean = $this->toolbox->getRedbean();
  6878. }
  6879. /**
  6880. * Turns an array (post/request array) into a collection of beans.
  6881. * Handy for turning forms into bean structures that can be stored with a
  6882. * single call.
  6883. *
  6884. * Typical usage:
  6885. *
  6886. * $struct = R::graph($_POST);
  6887. * R::store($struct);
  6888. *
  6889. * Example of a valid array:
  6890. *
  6891. * $form = array(
  6892. * 'type'=>'order',
  6893. * 'ownProduct'=>array(
  6894. * array('id'=>171,'type'=>'product'),
  6895. * ),
  6896. * 'ownCustomer'=>array(
  6897. * array('type'=>'customer','name'=>'Bill')
  6898. * ),
  6899. * 'sharedCoupon'=>array(
  6900. * array('type'=>'coupon','name'=>'123'),
  6901. * array('type'=>'coupon','id'=>3)
  6902. * )
  6903. * );
  6904. *
  6905. * Each entry in the array will become a property of the bean.
  6906. * The array needs to have a type-field indicating the type of bean it is
  6907. * going to be. The array can have nested arrays. A nested array has to be
  6908. * named conform the bean-relation conventions, i.e. ownPage/sharedPage
  6909. * each entry in the nested array represents another bean.
  6910. *
  6911. * @param array $array array to be turned into a bean collection
  6912. * @param boolean $filterEmpty whether you want to exclude empty beans
  6913. *
  6914. * @return array $beans beans
  6915. */
  6916. public function graph( $array, $filterEmpty = false ) {
  6917. $beans = array();
  6918. if (is_array($array) && isset($array['type'])) {
  6919. $type = $array['type'];
  6920. unset($array['type']);
  6921. //Do we need to load the bean?
  6922. if (isset($array['id'])) {
  6923. $id = (int) $array['id'];
  6924. $bean = $this->redbean->load($type,$id);
  6925. }
  6926. else {
  6927. $bean = $this->redbean->dispense($type);
  6928. }
  6929. foreach($array as $property=>$value) {
  6930. if (is_array($value)) {
  6931. $bean->$property = $this->graph($value,$filterEmpty);
  6932. }
  6933. else {
  6934. if($value == '' && self::$useNULLForEmptyString){
  6935. $bean->$property = null;
  6936. }
  6937. else
  6938. $bean->$property = $value;
  6939. }
  6940. }
  6941. return $bean;
  6942. }
  6943. elseif (is_array($array)) {
  6944. foreach($array as $key=>$value) {
  6945. $listBean = $this->graph($value,$filterEmpty);
  6946. if (!($listBean instanceof RedBean_OODBBean)) {
  6947. throw new RedBean_Exception_Security('Expected bean but got :'.gettype($listBean));
  6948. }
  6949. if ($listBean->isEmpty()) {
  6950. if (!$filterEmpty) {
  6951. $beans[$key] = $listBean;
  6952. }
  6953. }
  6954. else {
  6955. $beans[$key] = $listBean;
  6956. }
  6957. }
  6958. return $beans;
  6959. }
  6960. else {
  6961. throw new RedBean_Exception_Security('Expected array but got :'.gettype($array));
  6962. }
  6963. }
  6964. /**
  6965. * Toggles the use-NULL flag.
  6966. *
  6967. * @param boolean $yesNo
  6968. */
  6969. public function setUseNullFlag($yesNo) {
  6970. self::$useNULLForEmptyString = (boolean) $yesNo;
  6971. }
  6972. }
  6973. /**
  6974. * Query Logger
  6975. *
  6976. * @file RedBean/Plugin/QueryLogger.php
  6977. * @description Query logger, can be attached to an observer that
  6978. * signals the sql_exec event.
  6979. * @author Gabor de Mooij and the RedBeanPHP Community
  6980. * @license BSD/GPLv2
  6981. *
  6982. *
  6983. * (c) copyright G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community.
  6984. * This source file is subject to the BSD/GPLv2 License that is bundled
  6985. * with this source code in the file license.txt.
  6986. */
  6987. class RedBean_Plugin_QueryLogger implements RedBean_Observer {
  6988. /**
  6989. * @var array
  6990. * contains log messages
  6991. */
  6992. protected $logs = array();
  6993. /**
  6994. * Creates a new instance of the Query Logger and attaches
  6995. * this logger to the adapter.
  6996. *
  6997. * @static
  6998. * @param RedBean_Observable $adapter the adapter you want to attach to
  6999. *
  7000. * @return RedBean_Plugin_QueryLogger $querylogger instance of the Query Logger
  7001. */
  7002. public static function getInstanceAndAttach( RedBean_Observable $adapter ) {
  7003. $queryLog = new RedBean_Plugin_QueryLogger;
  7004. $adapter->addEventListener( 'sql_exec', $queryLog );
  7005. return $queryLog;
  7006. }
  7007. /**
  7008. * Singleton pattern
  7009. * Constructor - private
  7010. */
  7011. private function __construct(){}
  7012. /**
  7013. * Implementation of the onEvent() method for Observer interface.
  7014. * If a query gets executed this method gets invoked because the
  7015. * adapter will send a signal to the attached logger.
  7016. *
  7017. * @param string $eventName ID of the event (name)
  7018. * @param RedBean_DBAdapter $adapter adapter that sends the signal
  7019. *
  7020. * @return void
  7021. */
  7022. public function onEvent( $eventName, $adapter ) {
  7023. if ($eventName=='sql_exec') {
  7024. $this->logs[] = $adapter->getSQL();
  7025. }
  7026. }
  7027. /**
  7028. * Searches the logs for the given word and returns the entries found in
  7029. * the log container.
  7030. *
  7031. * @param string $word word to look for
  7032. *
  7033. * @return array $entries entries that contain the keyword
  7034. */
  7035. public function grep( $word ) {
  7036. $found = array();
  7037. foreach($this->logs as $log) {
  7038. if (strpos($log,$word)!==false) {
  7039. $found[] = $log;
  7040. }
  7041. }
  7042. return $found;
  7043. }
  7044. /**
  7045. * Returns all the logs.
  7046. *
  7047. * @return array $logs logs
  7048. */
  7049. public function getLogs() {
  7050. return $this->logs;
  7051. }
  7052. /**
  7053. * Clears the logs.
  7054. *
  7055. * @return void
  7056. */
  7057. public function clear() {
  7058. $this->logs = array();
  7059. }
  7060. }
  7061. /**
  7062. * TimeLine
  7063. *
  7064. * @file RedBean/Plugin/TimeLine.php
  7065. * @description Monitors schema changes to ease deployment.
  7066. * @author Gabor de Mooij and the RedBeanPHP Community
  7067. * @license BSD/GPLv2
  7068. *
  7069. * (c) copyright G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community.
  7070. * This source file is subject to the BSD/GPLv2 License that is bundled
  7071. * with this source code in the file license.txt.
  7072. */
  7073. class RedBean_Plugin_TimeLine extends RedBean_Plugin_QueryLogger {
  7074. /**
  7075. * Path to file to write SQL and comments to.
  7076. *
  7077. * @var string
  7078. */
  7079. protected $file;
  7080. /**
  7081. * Constructor.
  7082. * Requires a path to an existing and writable file.
  7083. *
  7084. * @param string $outputPath path to file to write schema changes to.
  7085. */
  7086. public function __construct($outputPath) {
  7087. if (!file_exists($outputPath) || !is_writable($outputPath))
  7088. throw new RedBean_Exception_Security('Cannot write to file: '.$outputPath);
  7089. $this->file = $outputPath;
  7090. }
  7091. /**
  7092. * Implementation of the onEvent() method for Observer interface.
  7093. * If a query gets executed this method gets invoked because the
  7094. * adapter will send a signal to the attached logger.
  7095. *
  7096. * @param string $eventName ID of the event (name)
  7097. * @param RedBean_DBAdapter $adapter adapter that sends the signal
  7098. *
  7099. * @return void
  7100. */
  7101. public function onEvent( $eventName, $adapter ) {
  7102. if ($eventName=='sql_exec') {
  7103. $sql = $adapter->getSQL();
  7104. $this->logs[] = $sql;
  7105. if (strpos($sql,'ALTER')===0) {
  7106. $write = "-- ".date('Y-m-d H:i')." | Altering table. \n";
  7107. $write .= $sql;
  7108. $write .= "\n\n";
  7109. }
  7110. if (strpos($sql,'CREATE')===0) {
  7111. $write = "-- ".date('Y-m-d H:i')." | Creating new table. \n";
  7112. $write .= $sql;
  7113. $write .= "\n\n";
  7114. }
  7115. if (isset($write)) {
  7116. file_put_contents($this->file,$write,FILE_APPEND);
  7117. }
  7118. }
  7119. }
  7120. }
  7121. /**
  7122. * RedBean Dependency Injector
  7123. *
  7124. * @file RedBean/DependencyInjector.php
  7125. * @description A default dependency injector that can be subclassed to
  7126. * suit your needs.
  7127. *
  7128. * @author Gabor de Mooij and the RedBeanPHP Community
  7129. * @license BSD/GPLv2
  7130. *
  7131. * copyright (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community
  7132. * This source file is subject to the BSD/GPLv2 License that is bundled
  7133. * with this source code in the file license.txt.
  7134. *
  7135. */
  7136. class RedBean_DependencyInjector {
  7137. /**
  7138. * List of dependencies.
  7139. * @var array
  7140. */
  7141. protected $dependencies = array();
  7142. /**
  7143. * Adds a dependency to the list.
  7144. * You can add dependencies using this method. Pass both the key of the
  7145. * dependency and the dependency itself. The key of the dependency is a
  7146. * name that should match the setter. For instance if you have a dependency
  7147. * class called My_Mailer and a setter on the model called setMailSystem
  7148. * you should pass an instance of My_Mailer with key MailSystem.
  7149. * The injector will now look for a setter called setMailSystem.
  7150. *
  7151. * @param string $dependencyID name of the dependency (should match setter)
  7152. * @param mixed $dependency the service to be injected
  7153. */
  7154. public function addDependency($dependencyID,$dependency) {
  7155. $this->dependencies[$dependencyID] = $dependency;
  7156. }
  7157. /**
  7158. * Returns an instance of the class $modelClassName completely
  7159. * configured as far as possible with all the available
  7160. * service objects in the dependency list.
  7161. *
  7162. * @param string $modelClassName the name of the class of the model
  7163. *
  7164. * @return mixed $object the model/object
  7165. */
  7166. public function getInstance($modelClassName) {
  7167. $object = new $modelClassName;
  7168. if ($this->dependencies && is_array($this->dependencies)) {
  7169. foreach($this->dependencies as $key=>$dep) {
  7170. $depSetter = 'set'.$key;
  7171. if (method_exists($object,$depSetter)) {
  7172. $object->$depSetter($dep);
  7173. }
  7174. }
  7175. }
  7176. return $object;
  7177. }
  7178. }
  7179. /**
  7180. * RedBean Duplication Manager
  7181. *
  7182. * @file RedBean/DuplicationManager.php
  7183. * @description Creates deep copies of beans
  7184. *
  7185. * @author Gabor de Mooij and the RedBeanPHP Community
  7186. * @license BSD/GPLv2
  7187. *
  7188. * copyright (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community
  7189. * This source file is subject to the BSD/GPLv2 License that is bundled
  7190. * with this source code in the file license.txt.
  7191. *
  7192. */
  7193. class RedBean_DuplicationManager {
  7194. /**
  7195. * The Dup Manager requires a toolbox
  7196. * @var RedBean_Toolbox
  7197. */
  7198. protected $toolbox;
  7199. /**
  7200. * Association Manager
  7201. * @var RedBean_AssociationManager
  7202. */
  7203. protected $associationManager;
  7204. /**
  7205. * RedBeanPHP OODB instance
  7206. * @var RedBean_OODBBean
  7207. */
  7208. protected $redbean;
  7209. /**
  7210. * Constructor,
  7211. * creates a new instance of DupManager.
  7212. * @param RedBean_Toolbox $toolbox
  7213. */
  7214. public function __construct( RedBean_Toolbox $toolbox ) {
  7215. $this->toolbox = $toolbox;
  7216. $this->redbean = $toolbox->getRedBean();
  7217. $this->associationManager = $this->redbean->getAssociationManager();
  7218. }
  7219. /**
  7220. * Makes a copy of a bean. This method makes a deep copy
  7221. * of the bean.The copy will have the following features.
  7222. * - All beans in own-lists will be duplicated as well
  7223. * - All references to shared beans will be copied but not the shared beans themselves
  7224. * - All references to parent objects (_id fields) will be copied but not the parents themselves
  7225. * In most cases this is the desired scenario for copying beans.
  7226. * This function uses a trail-array to prevent infinite recursion, if a recursive bean is found
  7227. * (i.e. one that already has been processed) the ID of the bean will be returned.
  7228. * This should not happen though.
  7229. *
  7230. * Note:
  7231. * This function does a reflectional database query so it may be slow.
  7232. *
  7233. * Note:
  7234. * this function actually passes the arguments to a protected function called
  7235. * duplicate() that does all the work. This method takes care of creating a clone
  7236. * of the bean to avoid the bean getting tainted (triggering saving when storing it).
  7237. *
  7238. * @param RedBean_OODBBean $bean bean to be copied
  7239. * @param array $trail for internal usage, pass array()
  7240. * @param boolean $pid for internal usage
  7241. *
  7242. * @return array $copiedBean the duplicated bean
  7243. */
  7244. public function dup($bean,$trail=array(),$pid=false) {
  7245. $beanCopy = clone($bean);
  7246. return $this->duplicate($beanCopy,$trail,$pid);
  7247. }
  7248. /**
  7249. * Makes a copy of a bean. This method makes a deep copy
  7250. * of the bean.The copy will have the following features.
  7251. * - All beans in own-lists will be duplicated as well
  7252. * - All references to shared beans will be copied but not the shared beans themselves
  7253. * - All references to parent objects (_id fields) will be copied but not the parents themselves
  7254. * In most cases this is the desired scenario for copying beans.
  7255. * This function uses a trail-array to prevent infinite recursion, if a recursive bean is found
  7256. * (i.e. one that already has been processed) the ID of the bean will be returned.
  7257. * This should not happen though.
  7258. *
  7259. * Note:
  7260. * This function does a reflectional database query so it may be slow.
  7261. *
  7262. * @param RedBean_OODBBean $bean bean to be copied
  7263. * @param array $trail for internal usage, pass array()
  7264. * @param boolean $pid for internal usage
  7265. *
  7266. * @return array $copiedBean the duplicated bean
  7267. */
  7268. protected function duplicate($bean,$trail=array(),$pid=false) {
  7269. $type = $bean->getMeta('type');
  7270. $key = $type.$bean->getID();
  7271. if (isset($trail[$key])) return $bean;
  7272. $trail[$key]=$bean;
  7273. $copy =$this->redbean->dispense($type);
  7274. $copy->import( $bean->getProperties() );
  7275. $copy->id = 0;
  7276. $tables = $this->toolbox->getWriter()->getTables();
  7277. foreach($tables as $table) {
  7278. if (strpos($table,'_')!==false || $table==$type) continue;
  7279. $owned = 'own'.ucfirst($table);
  7280. $shared = 'shared'.ucfirst($table);
  7281. if ($beans = $bean->$owned) {
  7282. $copy->$owned = array();
  7283. foreach($beans as $subBean) {
  7284. array_push($copy->$owned,$this->duplicate($subBean,$trail,$pid));
  7285. }
  7286. }
  7287. $copy->setMeta('sys.shadow.'.$owned,null);
  7288. if ($beans = $bean->$shared) {
  7289. $copy->$shared = array();
  7290. foreach($beans as $subBean) {
  7291. array_push($copy->$shared,$subBean);
  7292. }
  7293. }
  7294. $copy->setMeta('sys.shadow.'.$shared,null);
  7295. }
  7296. if ($pid) $copy->id = $bean->id;
  7297. return $copy;
  7298. }
  7299. }
  7300. class R extends RedBean_Facade{
  7301. }