PageRenderTime 103ms CodeModel.GetById 23ms RepoModel.GetById 0ms app.codeStats 1ms

/redbean/rb.php

https://bitbucket.org/andreustimm/zurmo
PHP | 8670 lines | 3180 code | 737 blank | 4753 comment | 480 complexity | c8e426c85ddb14355681316e1fd202a1 MD5 | raw file
Possible License(s): BSD-3-Clause, GPL-2.0, LGPL-3.0, LGPL-2.1, BSD-2-Clause
  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. PDO::MYSQL_ATTR_LOCAL_INFILE => true,
  524. )
  525. );
  526. $this->pdo->setAttribute(PDO::ATTR_STRINGIFY_FETCHES, true);
  527. $this->isConnected = true;
  528. }
  529. /**
  530. * Binds parameters. This method binds parameters to a PDOStatement for
  531. * Query Execution. This method binds parameters as NULL, INTEGER or STRING
  532. * and supports both named keys and question mark keys.
  533. *
  534. * @param PDOStatement $s PDO Statement instance
  535. * @param array $aValues values that need to get bound to the statement
  536. *
  537. * @return void
  538. */
  539. protected function bindParams($s,$aValues) {
  540. foreach($aValues as $key=>&$value) {
  541. if (is_integer($key)) {
  542. if (is_null($value)){
  543. $s->bindValue($key+1,null,PDO::PARAM_NULL);
  544. }
  545. elseif (!$this->flagUseStringOnlyBinding && RedBean_QueryWriter_AQueryWriter::canBeTreatedAsInt($value) && $value < 2147483648) {
  546. $s->bindParam($key+1,$value,PDO::PARAM_INT);
  547. }
  548. else {
  549. $s->bindParam($key+1,$value,PDO::PARAM_STR);
  550. }
  551. }
  552. else {
  553. if (is_null($value)){
  554. $s->bindValue($key,null,PDO::PARAM_NULL);
  555. }
  556. elseif (!$this->flagUseStringOnlyBinding && RedBean_QueryWriter_AQueryWriter::canBeTreatedAsInt($value) && $value < 2147483648) {
  557. $s->bindParam($key,$value,PDO::PARAM_INT);
  558. }
  559. else {
  560. $s->bindParam($key,$value,PDO::PARAM_STR);
  561. }
  562. }
  563. }
  564. }
  565. /**
  566. * Runs a query. Internal function, available for subclasses. This method
  567. * runs the actual SQL query and binds a list of parameters to the query.
  568. * slots. The result of the query will be stored in the protected property
  569. * $rs (always array). The number of rows affected (result of rowcount, if supported by database)
  570. * is stored in protected property $affected_rows. If the debug flag is set
  571. * this function will send debugging output to screen buffer.
  572. *
  573. * @throws RedBean_Exception_SQL
  574. *
  575. * @param string $sql the SQL string to be send to database server
  576. * @param array $aValues the values that need to get bound to the query slots
  577. */
  578. protected function runQuery($sql,$aValues) {
  579. $this->connect();
  580. if ($this->debug && $this->logger) {
  581. $this->logger->log($sql, $aValues);
  582. }
  583. try {
  584. if (strpos('pgsql',$this->dsn)===0) {
  585. $s = $this->pdo->prepare($sql, array(PDO::PGSQL_ATTR_DISABLE_NATIVE_PREPARED_STATEMENT => true));
  586. }
  587. else {
  588. $s = $this->pdo->prepare($sql);
  589. }
  590. $this->bindParams( $s, $aValues );
  591. $s->execute();
  592. $this->affected_rows = $s->rowCount();
  593. if ($s->columnCount()) {
  594. $this->rs = $s->fetchAll();
  595. if ($this->debug && $this->logger) $this->logger->log('resultset: ' . count($this->rs) . ' rows');
  596. }
  597. else {
  598. $this->rs = array();
  599. }
  600. }catch(PDOException $e) {
  601. //Unfortunately the code field is supposed to be int by default (php)
  602. //So we need a property to convey the SQL State code.
  603. $x = new RedBean_Exception_SQL( $e->getMessage(), 0);
  604. $x->setSQLState( $e->getCode() );
  605. throw $x;
  606. }
  607. }
  608. /**
  609. * Runs a query and fetches results as a multi dimensional array.
  610. *
  611. * @param string $sql SQL to be executed
  612. *
  613. * @return array $results result
  614. */
  615. public function GetAll( $sql, $aValues=array() ) {
  616. $this->runQuery($sql,$aValues);
  617. return $this->rs;
  618. }
  619. /**
  620. * Runs a query and fetches results as a column.
  621. *
  622. * @param string $sql SQL Code to execute
  623. *
  624. * @return array $results Resultset
  625. */
  626. public function GetCol($sql, $aValues=array()) {
  627. $rows = $this->GetAll($sql,$aValues);
  628. $cols = array();
  629. if ($rows && is_array($rows) && count($rows)>0) {
  630. foreach ($rows as $row) {
  631. $cols[] = array_shift($row);
  632. }
  633. }
  634. return $cols;
  635. }
  636. /**
  637. * Runs a query an returns results as a single cell.
  638. *
  639. * @param string $sql SQL to execute
  640. *
  641. * @return mixed $cellvalue result cell
  642. */
  643. public function GetCell($sql, $aValues=array()) {
  644. $arr = $this->GetAll($sql,$aValues);
  645. $row1 = array_shift($arr);
  646. $col1 = array_shift($row1);
  647. return $col1;
  648. }
  649. /**
  650. * Runs a query and returns a flat array containing the values of
  651. * one row.
  652. *
  653. * @param string $sql SQL to execute
  654. *
  655. * @return array $row result row
  656. */
  657. public function GetRow($sql, $aValues=array()) {
  658. $arr = $this->GetAll($sql, $aValues);
  659. return array_shift($arr);
  660. }
  661. /**
  662. * Executes SQL code and allows key-value binding.
  663. * This function allows you to provide an array with values to bind
  664. * to query parameters. For instance you can bind values to question
  665. * marks in the query. Each value in the array corresponds to the
  666. * question mark in the query that matches the position of the value in the
  667. * array. You can also bind values using explicit keys, for instance
  668. * array(":key"=>123) will bind the integer 123 to the key :key in the
  669. * SQL. This method has no return value.
  670. *
  671. * @param string $sql SQL Code to execute
  672. * @param array $aValues Values to bind to SQL query
  673. *
  674. * @return void
  675. */
  676. public function Execute( $sql, $aValues=array() ) {
  677. $this->runQuery($sql,$aValues);
  678. return $this->affected_rows;
  679. }
  680. /**
  681. * Escapes a string for use in SQL using the currently selected
  682. * PDO driver.
  683. *
  684. * @param string $string string to be escaped
  685. *
  686. * @return string $string escaped string
  687. */
  688. public function Escape( $str ) {
  689. $this->connect();
  690. return substr(substr($this->pdo->quote($str), 1), 0, -1);
  691. }
  692. /**
  693. * Returns the latest insert ID if driver does support this
  694. * feature.
  695. *
  696. * @return integer $id primary key ID
  697. */
  698. public function GetInsertID() {
  699. $this->connect();
  700. return (int) $this->pdo->lastInsertId();
  701. }
  702. /**
  703. * Returns the number of rows affected by the most recent query
  704. * if the currently selected PDO driver supports this feature.
  705. *
  706. * @return integer $numOfRows number of rows affected
  707. */
  708. public function Affected_Rows() {
  709. $this->connect();
  710. return (int) $this->affected_rows;
  711. }
  712. /**
  713. * Toggles debug mode. In debug mode the driver will print all
  714. * SQL to the screen together with some information about the
  715. * results. All SQL code that passes through the driver will be
  716. * passes on to the screen for inspection.
  717. * This method has no return value.
  718. *
  719. * Additionally you can inject RedBean_ILogger implementation
  720. * where you can define your own log() method
  721. *
  722. * @param boolean $trueFalse turn on/off
  723. * @param RedBean_ILogger $logger
  724. *
  725. * @return void
  726. */
  727. public function setDebugMode( $tf, $logger = NULL ) {
  728. $this->connect();
  729. $this->debug = (bool)$tf;
  730. if ($this->debug and !$logger) $logger = new RedBean_Logger();
  731. $this->setLogger($logger);
  732. }
  733. /**
  734. * Injects RedBean_ILogger object.
  735. *
  736. * @param RedBean_ILogger $logger
  737. */
  738. public function setLogger( RedBean_ILogger $logger ) {
  739. $this->logger = $logger;
  740. }
  741. /**
  742. * Gets RedBean_ILogger object.
  743. *
  744. * @return RedBean_ILogger
  745. */
  746. public function getLogger() {
  747. return $this->logger;
  748. }
  749. /**
  750. * Starts a transaction.
  751. * This method is part of the transaction mechanism of
  752. * RedBeanPHP. All queries in a transaction are executed together.
  753. * In case of an error all commands will be rolled back so none of the
  754. * SQL in the transaction will affect the DB. Using transactions is
  755. * considered best practice.
  756. * This method has no return value.
  757. *
  758. * @return void
  759. */
  760. public function StartTrans() {
  761. $this->connect();
  762. $this->pdo->beginTransaction();
  763. }
  764. /**
  765. * Commits a transaction.
  766. * This method is part of the transaction mechanism of
  767. * RedBeanPHP. All queries in a transaction are executed together.
  768. * In case of an error all commands will be rolled back so none of the
  769. * SQL in the transaction will affect the DB. Using transactions is
  770. * considered best practice.
  771. * This method has no return value.
  772. *
  773. * @return void
  774. */
  775. public function CommitTrans() {
  776. $this->connect();
  777. $this->pdo->commit();
  778. }
  779. /**
  780. * Rolls back a transaction.
  781. * This method is part of the transaction mechanism of
  782. * RedBeanPHP. All queries in a transaction are executed together.
  783. * In case of an error all commands will be rolled back so none of the
  784. * SQL in the transaction will affect the DB. Using transactions is
  785. * considered best practice.
  786. * This method has no return value.
  787. *
  788. * @return void
  789. */
  790. public function FailTrans() {
  791. $this->connect();
  792. $this->pdo->rollback();
  793. }
  794. /**
  795. * Returns the name of the database type/brand: i.e. mysql, db2 etc.
  796. *
  797. * @return string $typeName database identification
  798. */
  799. public function getDatabaseType() {
  800. $this->connect();
  801. return $this->pdo->getAttribute(PDO::ATTR_DRIVER_NAME);
  802. }
  803. /**
  804. * Returns the version number of the database.
  805. *
  806. * @return mixed $version version number of the database
  807. */
  808. public function getDatabaseVersion() {
  809. $this->connect();
  810. return $this->pdo->getAttribute(PDO::ATTR_CLIENT_VERSION);
  811. }
  812. /**
  813. * Returns the underlying PHP PDO instance.
  814. *
  815. * @return PDO $pdo PDO instance used by PDO wrapper
  816. */
  817. public function getPDO() {
  818. $this->connect();
  819. return $this->pdo;
  820. }
  821. /**
  822. * Closes database connection by destructing PDO.
  823. */
  824. public function close() {
  825. $this->pdo = null;
  826. $this->isConnected = false;
  827. }
  828. /**
  829. * Returns TRUE if the current PDO instance is connected.
  830. *
  831. * @return boolean $yesNO
  832. */
  833. public function isConnected() {
  834. if (!$this->isConnected && !$this->pdo) return false;
  835. return true;
  836. }
  837. }
  838. /**
  839. * RedBean_OODBBean (Object Oriented DataBase Bean)
  840. *
  841. * @file RedBean/RedBean_OODBBean.php
  842. * @description The Bean class used for passing information
  843. *
  844. * @author Gabor de Mooij and the RedBeanPHP community
  845. * @license BSD/GPLv2
  846. *
  847. *
  848. * copyright (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community.
  849. * This source file is subject to the BSD/GPLv2 License that is bundled
  850. * with this source code in the file license.txt.
  851. */
  852. class RedBean_OODBBean implements IteratorAggregate, ArrayAccess, Countable {
  853. /**
  854. * Reference to NULL property for magic getter.
  855. * @var Null $null
  856. */
  857. private $null = null;
  858. /**
  859. * Properties of the bean. These are kept in a private
  860. * array called properties and exposed through the array interface.
  861. * @var array $properties
  862. */
  863. private $properties = array();
  864. /**
  865. * Meta Data storage. This is the internal property where all
  866. * Meta information gets stored.
  867. * @var array
  868. */
  869. private $__info = NULL;
  870. /**
  871. * Contains a BeanHelper to access service objects like
  872. * te association manager and OODB.
  873. * @var RedBean_BeanHelper
  874. */
  875. private $beanHelper = NULL;
  876. /**
  877. * Contains the latest Fetch Type.
  878. * A Fetch Type is a preferred type for the next nested bean.
  879. * @var null
  880. */
  881. private $fetchType = NULL;
  882. /** Returns the alias for a type
  883. *
  884. * @param $type aliased type
  885. *
  886. * @return string $type type
  887. */
  888. private function getAlias( $type ) {
  889. if ($this->fetchType) {
  890. $type = $this->fetchType;
  891. $this->fetchType = null;
  892. }
  893. return $type;
  894. }
  895. /**
  896. * Sets the Bean Helper. Normally the Bean Helper is set by OODB.
  897. * Here you can change the Bean Helper. The Bean Helper is an object
  898. * providing access to a toolbox for the bean necessary to retrieve
  899. * nested beans (bean lists: ownBean,sharedBean) without the need to
  900. * rely on static calls to the facade (or make this class dep. on OODB).
  901. *
  902. * @param RedBean_IBeanHelper $helper
  903. * @return void
  904. */
  905. public function setBeanHelper(RedBean_IBeanHelper $helper) {
  906. $this->beanHelper = $helper;
  907. }
  908. /**
  909. * Returns an ArrayIterator so you can treat the bean like
  910. * an array with the properties container as its contents.
  911. *
  912. * @return ArrayIterator $arrayIt an array iterator instance with $properties
  913. */
  914. public function getIterator() {
  915. return new ArrayIterator($this->properties);
  916. }
  917. /**
  918. * Imports all values in associative array $array. Every key is used
  919. * for a property and every value will be assigned to the property
  920. * identified by the key. So basically this method converts the
  921. * associative array to a bean by loading the array. You can filter
  922. * the values using the $selection parameter. If $selection is boolean
  923. * false, no filtering will be applied. If $selection is an array
  924. * only the properties specified (as values) in the $selection
  925. * array will be taken into account. To skip a property, omit it from
  926. * the $selection array. Also, instead of providing an array you may
  927. * pass a comma separated list of property names. This method is
  928. * chainable because it returns its own object.
  929. * Imports data into bean
  930. *
  931. * @param array $array what you want to import
  932. * @param string|array $selection selection of values
  933. * @param boolean $notrim if TRUE values will not be trimmed
  934. *
  935. * @return RedBean_OODBBean $this
  936. */
  937. public function import( $arr, $selection=false, $notrim=false ) {
  938. if (is_string($selection)) $selection = explode(',',$selection);
  939. //trim whitespaces
  940. if (!$notrim && is_array($selection)) foreach($selection as $k=>$s){ $selection[$k]=trim($s); }
  941. foreach($arr as $k=>$v) {
  942. if ($k!='__info') {
  943. if (!$selection || ($selection && in_array($k,$selection))) {
  944. $this->$k = $v;
  945. }
  946. }
  947. }
  948. return $this;
  949. }
  950. /**
  951. * Very superficial export function
  952. * @return array $properties
  953. */
  954. public function getProperties() {
  955. return $this->properties;
  956. }
  957. /**
  958. * Exports the bean as an array.
  959. * This function exports the contents of a bean to an array and returns
  960. * the resulting array. If $meta eq uals boolean TRUE, then the array will
  961. * also contain the __info section containing the meta data inside the
  962. * RedBean_OODBBean Bean object.
  963. * @param boolean $meta
  964. * @return array $arr
  965. */
  966. public function export($meta = false) {
  967. //$arr = $this->properties;
  968. $arr=array();
  969. foreach($this as $k=>$v) {
  970. if (is_array($v)) foreach($v as $i=>$b) $v[$i]=$b->export();
  971. $arr[$k] = $v;
  972. }
  973. if ($meta) $arr['__info'] = $this->__info;
  974. return $arr;
  975. }
  976. /**
  977. * Exports the bean to an object.
  978. * This function exports the contents of a bean to an object.
  979. * @param object $obj
  980. * @return array $arr
  981. */
  982. public function exportToObj($obj) {
  983. foreach($this->properties as $k=>$v) {
  984. if (!is_array($v) && !is_object($v))
  985. $obj->$k = $v;
  986. }
  987. }
  988. /**
  989. * Implements isset() function for use as an array.
  990. * Returns whether bean has an element with key
  991. * named $property. Returns TRUE if such an element exists
  992. * and FALSE otherwise.
  993. * @param string $property
  994. * @return boolean $hasProperty
  995. */
  996. public function __isset($property) {
  997. return (isset($this->properties[$property]));
  998. }
  999. /**
  1000. * Returns the ID of the bean no matter what the ID field is.
  1001. *
  1002. * @return string $id record Identifier for bean
  1003. */
  1004. public function getID() {
  1005. return (string) $this->id;
  1006. }
  1007. /**
  1008. * Unsets a property. This method will load the property first using
  1009. * __get.
  1010. *
  1011. * @param string $property property
  1012. *
  1013. * @return void
  1014. */
  1015. public function __unset($property) {
  1016. $this->__get($property);
  1017. $fieldLink = $property.'_id';
  1018. if (isset($this->$fieldLink)) {
  1019. //wanna unset a bean reference?
  1020. $this->$fieldLink = null;
  1021. }
  1022. if ((isset($this->properties[$property]))) {
  1023. unset($this->properties[$property]);
  1024. }
  1025. }
  1026. /**
  1027. * Removes a property from the properties list without invoking
  1028. * an __unset on the bean.
  1029. *
  1030. * @param string $property property that needs to be unset
  1031. *
  1032. * @return void
  1033. */
  1034. public function removeProperty( $property ) {
  1035. unset($this->properties[$property]);
  1036. }
  1037. /**
  1038. * Magic Getter. Gets the value for a specific property in the bean.
  1039. * If the property does not exist this getter will make sure no error
  1040. * occurs. This is because RedBean allows you to query (probe) for
  1041. * properties. If the property can not be found this method will
  1042. * return NULL instead.
  1043. * @param string $property
  1044. * @return mixed $value
  1045. */
  1046. public function &__get( $property ) {
  1047. if ($this->beanHelper)
  1048. $toolbox = $this->beanHelper->getToolbox();
  1049. if (!isset($this->properties[$property])) {
  1050. $fieldLink = $property.'_id';
  1051. /**
  1052. * All this magic can be become very complex quicly. For instance,
  1053. * my PHP CLI produced a segfault while testing this code. Turns out that
  1054. * if fieldlink equals idfield, scripts tend to recusrively load beans and
  1055. * instead of giving a clue they simply crash and burn isnt that nice?
  1056. */
  1057. if (isset($this->$fieldLink) && $fieldLink != $this->getMeta('sys.idfield')) {
  1058. $this->setMeta('tainted',true);
  1059. $type = $this->getAlias($property);
  1060. $targetType = $this->properties[$fieldLink];
  1061. $bean = $toolbox->getRedBean()->load($type,$targetType);
  1062. //return $bean;
  1063. $this->properties[$property] = $bean;
  1064. return $this->properties[$property];
  1065. }
  1066. if (strpos($property,'own')===0) {
  1067. $firstCharCode = ord(substr($property,3,1));
  1068. if ($firstCharCode>=65 && $firstCharCode<=90) {
  1069. $type = (__lcfirst(str_replace('own','',$property)));
  1070. $myFieldLink = $this->getMeta('type').'_id';
  1071. $beans = $toolbox->getRedBean()->find($type,array(),array(" $myFieldLink = ? ",array($this->getID())));
  1072. $this->properties[$property] = $beans;
  1073. $this->setMeta('sys.shadow.'.$property,$beans);
  1074. $this->setMeta('tainted',true);
  1075. return $this->properties[$property];
  1076. }
  1077. }
  1078. if (strpos($property,'shared')===0) {
  1079. $firstCharCode = ord(substr($property,6,1));
  1080. if ($firstCharCode>=65 && $firstCharCode<=90) {
  1081. $type = (__lcfirst(str_replace('shared','',$property)));
  1082. $keys = $toolbox->getRedBean()->getAssociationManager()->related($this,$type);
  1083. if (!count($keys)) $beans = array(); else
  1084. $beans = $toolbox->getRedBean()->batch($type,$keys);
  1085. $this->properties[$property] = $beans;
  1086. $this->setMeta('sys.shadow.'.$property,$beans);
  1087. $this->setMeta('tainted',true);
  1088. return $this->properties[$property];
  1089. }
  1090. }
  1091. return $this->null;
  1092. }
  1093. return $this->properties[$property];
  1094. }
  1095. /**
  1096. * Magic Setter. Sets the value for a specific property.
  1097. * This setter acts as a hook for OODB to mark beans as tainted.
  1098. * The tainted meta property can be retrieved using getMeta("tainted").
  1099. * The tainted meta property indicates whether a bean has been modified and
  1100. * can be used in various caching mechanisms.
  1101. * @param string $property
  1102. * @param mixed $value
  1103. */
  1104. public function __set($property,$value) {
  1105. $this->__get($property);
  1106. $this->setMeta('tainted',true);
  1107. $linkField = $property.'_id';
  1108. if (isset($this->properties[$linkField]) && !($value instanceof RedBean_OODBBean)) {
  1109. if (is_null($value) || $value === false) {
  1110. return $this->__unset($property);
  1111. }
  1112. else {
  1113. throw new RedBean_Exception_Security('Cannot cast to bean.');
  1114. }
  1115. }
  1116. if ($value===false) {
  1117. $value = '0';
  1118. }
  1119. if ($value===true) {
  1120. $value = '1';
  1121. }
  1122. $this->properties[$property] = $value;
  1123. }
  1124. /**
  1125. * Returns the value of a meta property. A meta property
  1126. * contains extra information about the bean object that will not
  1127. * get stored in the database. Meta information is used to instruct
  1128. * RedBean as well as other systems how to deal with the bean.
  1129. * For instance: $bean->setMeta("buildcommand.unique", array(
  1130. * array("column1", "column2", "column3") ) );
  1131. * Will add a UNIQUE constaint for the bean on columns: column1, column2 and
  1132. * column 3.
  1133. * To access a Meta property we use a dot separated notation.
  1134. * If the property cannot be found this getter will return NULL instead.
  1135. * @param string $path
  1136. * @param mixed $default
  1137. * @return mixed $value
  1138. */
  1139. public function getMeta($path,$default = NULL) {
  1140. return (isset($this->__info[$path])) ? $this->__info[$path] : $default;
  1141. }
  1142. /**
  1143. * Stores a value in the specified Meta information property. $value contains
  1144. * the value you want to store in the Meta section of the bean and $path
  1145. * specifies the dot separated path to the property. For instance "my.meta.property".
  1146. * If "my" and "meta" do not exist they will be created automatically.
  1147. * @param string $path
  1148. * @param mixed $value
  1149. */
  1150. public function setMeta($path,$value) {
  1151. $this->__info[$path] = $value;
  1152. }
  1153. /**
  1154. * Copies the meta information of the specified bean
  1155. * This is a convenience method to enable you to
  1156. * exchange meta information easily.
  1157. * @param RedBean_OODBBean $bean
  1158. * @return RedBean_OODBBean
  1159. */
  1160. public function copyMetaFrom(RedBean_OODBBean $bean) {
  1161. $this->__info = $bean->__info;
  1162. return $this;
  1163. }
  1164. /**
  1165. * Reroutes a call to Model if exists. (new fuse)
  1166. * @param string $method
  1167. * @param array $args
  1168. * @return mixed $mixed
  1169. */
  1170. public function __call($method, $args) {
  1171. return null;
  1172. if (!isset($this->__info['model'])) {
  1173. $model = $this->beanHelper->getModelForBean($this);
  1174. if (!$model) return;
  1175. $this->__info['model'] = $model;
  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. if (!$delete)
  2286. {
  2287. return $rows;
  2288. }
  2289. }
  2290. /**
  2291. * This method removes all beans of a certain type.
  2292. * This methods accepts a type and infers the corresponding table name.
  2293. *
  2294. * @param string $type bean type
  2295. *
  2296. * @return void
  2297. */
  2298. public function wipe($type) {
  2299. $table = $type;
  2300. $table = $this->safeTable($table);
  2301. $sql = "TRUNCATE $table ";
  2302. $this->adapter->exec($sql);
  2303. }
  2304. /**
  2305. * Counts rows in a table.
  2306. *
  2307. * @param string $beanType
  2308. *
  2309. * @return integer $numRowsFound
  2310. */
  2311. public function count($beanType) {
  2312. $sql = "SELECT count(*) FROM {$this->safeTable($beanType)} ";
  2313. return (int) $this->adapter->getCell($sql);
  2314. }
  2315. /**
  2316. * This method should add an index to a type and field with name
  2317. * $name.
  2318. * This methods accepts a type and infers the corresponding table name.
  2319. *
  2320. * @param $type type to add index to
  2321. * @param $name name of the new index
  2322. * @param $column field to index
  2323. *
  2324. * @return void
  2325. */
  2326. public function addIndex($type, $name, $column) {
  2327. $table = $type;
  2328. $table = $this->safeTable($table);
  2329. $name = preg_replace('/\W/','',$name);
  2330. $column = $this->safeColumn($column);
  2331. try{ $this->adapter->exec("CREATE INDEX $name ON $table ($column) "); }catch(Exception $e){}
  2332. }
  2333. /**
  2334. * This is a utility service method publicly available.
  2335. * It allows you to check whether you can safely treat an certain value as an integer by
  2336. * comparing an int-valled string representation with a default string casted string representation and
  2337. * a ctype-digit check. It does not take into account numerical limitations (X-bit INT), just that it
  2338. * can be treated like an INT. This is useful for binding parameters to query statements like
  2339. * Query Writers and drivers can do.
  2340. *
  2341. * @static
  2342. *
  2343. * @param string $value string representation of a certain value
  2344. *
  2345. * @return boolean $value boolean result of analysis
  2346. */
  2347. public static function canBeTreatedAsInt( $value ) {
  2348. return (boolean) (ctype_digit(strval($value)) && strval($value)===strval(intval($value)));
  2349. }
  2350. /**
  2351. * This method adds a foreign key from type and field to
  2352. * target type and target field.
  2353. * The foreign key is created without an action. On delete/update
  2354. * no action will be triggered. The FK is only used to allow database
  2355. * tools to generate pretty diagrams and to make it easy to add actions
  2356. * later on.
  2357. * This methods accepts a type and infers the corresponding table name.
  2358. *
  2359. *
  2360. * @param string $type type that will have a foreign key field
  2361. * @param string $targetType points to this type
  2362. * @param string $field field that contains the foreign key value
  2363. * @param string $targetField field where the fk points to
  2364. *
  2365. * @return void
  2366. */
  2367. public function addFK( $type, $targetType, $field, $targetField, $isDependent = false) {
  2368. return true;
  2369. $table = $this->safeTable($type);
  2370. $tableNoQ = $this->safeTable($type,true);
  2371. $targetTable = $this->safeTable($targetType);
  2372. $column = $this->safeColumn($field);
  2373. $columnNoQ = $this->safeColumn($field,true);
  2374. $targetColumn = $this->safeColumn($targetField);
  2375. $targetColumnNoQ = $this->safeColumn($targetField,true);
  2376. $db = $this->adapter->getCell('select database()');
  2377. $fkName = 'fk_'.$tableNoQ.'_'.$columnNoQ.'_'.$targetColumnNoQ.($isDependent ? '_casc':'');
  2378. $cName = 'cons_'.$fkName;
  2379. $cfks = $this->adapter->getCell("
  2380. SELECT CONSTRAINT_NAME
  2381. FROM information_schema.KEY_COLUMN_USAGE
  2382. WHERE TABLE_SCHEMA ='$db' AND TABLE_NAME = '$tableNoQ' AND COLUMN_NAME = '$columnNoQ' AND
  2383. CONSTRAINT_NAME <>'PRIMARY' AND REFERENCED_TABLE_NAME is not null
  2384. ");
  2385. $flagAddKey = false;
  2386. try{
  2387. //No keys
  2388. if (!$cfks) {
  2389. $flagAddKey = true; //go get a new key
  2390. }
  2391. //has fk, but different setting, --remove
  2392. if ($cfks && $cfks!=$cName) {
  2393. $this->adapter->exec("ALTER TABLE $table DROP FOREIGN KEY $cfks ");
  2394. $flagAddKey = true; //go get a new key.
  2395. }
  2396. if ($flagAddKey) {
  2397. $this->adapter->exec("ALTER TABLE $table
  2398. ADD CONSTRAINT $cName FOREIGN KEY $fkName ( $column ) REFERENCES $targetTable (
  2399. $targetColumn) ON DELETE ".($isDependent ? 'CASCADE':'SET NULL').' ON UPDATE SET NULL ;');
  2400. }
  2401. }
  2402. catch(Exception $e) { } //Failure of fk-constraints is not a problem
  2403. }
  2404. /**
  2405. * Returns the format for link tables.
  2406. * Given an array containing two type names this method returns the
  2407. * name of the link table to be used to store and retrieve
  2408. * association records.
  2409. *
  2410. * @param array $types two types array($type1,$type2)
  2411. *
  2412. * @return string $linktable name of the link table
  2413. */
  2414. public static function getAssocTableFormat($types) {
  2415. sort($types);
  2416. return ( implode('_', $types) );
  2417. }
  2418. /**
  2419. * Adds a constraint. If one of the beans gets trashed
  2420. * the other, related bean should be removed as well.
  2421. *
  2422. * @param RedBean_OODBBean $bean1 first bean
  2423. * @param RedBean_OODBBean $bean2 second bean
  2424. * @param bool $dontCache by default we use a cache, TRUE = NO CACHING (optional)
  2425. *
  2426. * @return void
  2427. */
  2428. public function addConstraint( RedBean_OODBBean $bean1, RedBean_OODBBean $bean2) {
  2429. $table1 = $bean1->getMeta('type');
  2430. $table2 = $bean2->getMeta('type');
  2431. $writer = $this;
  2432. $adapter = $this->adapter;
  2433. $table = RedBean_QueryWriter_AQueryWriter::getAssocTableFormat( array( $table1,$table2) );
  2434. $property1 = $bean1->getMeta('type') . '_id';
  2435. $property2 = $bean2->getMeta('type') . '_id';
  2436. if ($property1==$property2) $property2 = $bean2->getMeta('type').'2_id';
  2437. $table = $adapter->escape($table);
  2438. $table1 = $adapter->escape($table1);
  2439. $table2 = $adapter->escape($table2);
  2440. $property1 = $adapter->escape($property1);
  2441. $property2 = $adapter->escape($property2);
  2442. //Dispatch to right method
  2443. return $this->constrain($table, $table1, $table2, $property1, $property2);
  2444. }
  2445. /**
  2446. * Checks whether a value starts with zeros. In this case
  2447. * the value should probably be stored using a text datatype instead of a
  2448. * numerical type in order to preserve the zeros.
  2449. *
  2450. * @param string $value value to be checked.
  2451. */
  2452. protected function startsWithZeros($value) {
  2453. $value = strval($value);
  2454. if (strlen($value)>1 && strpos($value,'0')===0 && strpos($value,'0.')!==0) {
  2455. return true;
  2456. }
  2457. else {
  2458. return false;
  2459. }
  2460. }
  2461. /**
  2462. * Returns a modified value from ScanType.
  2463. * Used for special types.
  2464. *
  2465. * @return mixed $value changed value
  2466. */
  2467. public function getValue(){
  2468. return $this->svalue;
  2469. }
  2470. }
  2471. /**
  2472. * RedBean MySQLWriter
  2473. *
  2474. * @file RedBean/QueryWriter/MySQL.php
  2475. * @description Represents a MySQL Database to RedBean
  2476. * To write a driver for a different database for RedBean
  2477. * you should only have to change this file.
  2478. * @author Gabor de Mooij and the RedBeanPHP Community
  2479. * @license BSD/GPLv2
  2480. *
  2481. *
  2482. * (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community.
  2483. * This source file is subject to the BSD/GPLv2 License that is bundled
  2484. * with this source code in the file license.txt.
  2485. */
  2486. class RedBean_QueryWriter_MySQL extends RedBean_QueryWriter_AQueryWriter implements RedBean_QueryWriter {
  2487. /**
  2488. * Here we describe the datatypes that RedBean
  2489. * Uses internally. If you write a QueryWriter for
  2490. * RedBean you should provide a list of types like this.
  2491. */
  2492. /**
  2493. * DATA TYPE
  2494. * Boolean Data type
  2495. * @var integer
  2496. */
  2497. const C_DATATYPE_BOOL = 0;
  2498. /**
  2499. *
  2500. * DATA TYPE
  2501. * Unsigned 8BIT Integer
  2502. * @var integer
  2503. */
  2504. const C_DATATYPE_UINT8 = 1;
  2505. /**
  2506. *
  2507. * DATA TYPE
  2508. * Unsigned 32BIT Integer
  2509. * @var integer
  2510. */
  2511. const C_DATATYPE_UINT32 = 2;
  2512. /**
  2513. * DATA TYPE
  2514. * Double precision floating point number and
  2515. * negative numbers.
  2516. * @var integer
  2517. */
  2518. const C_DATATYPE_DOUBLE = 3;
  2519. /**
  2520. * DATA TYPE
  2521. * Standard Text column (like varchar255)
  2522. * At least 8BIT character support.
  2523. * @var integer
  2524. */
  2525. const C_DATATYPE_TEXT8 = 4;
  2526. /**
  2527. * DATA TYPE
  2528. * Long text column (16BIT)
  2529. * @var integer
  2530. */
  2531. const C_DATATYPE_TEXT16 = 5;
  2532. /**
  2533. *
  2534. * DATA TYPE
  2535. * 32BIT long textfield (number of characters can be as high as 32BIT) Data type
  2536. * This is the biggest column that RedBean supports. If possible you may write
  2537. * an implementation that stores even bigger values.
  2538. * @var integer
  2539. */
  2540. const C_DATATYPE_TEXT32 = 6;
  2541. /**
  2542. * Special type date for storing date values: YYYY-MM-DD
  2543. * @var integer
  2544. */
  2545. const C_DATATYPE_SPECIAL_DATE = 80;
  2546. /**
  2547. * Special type datetime for store date-time values: YYYY-MM-DD HH:II:SS
  2548. * @var integer
  2549. */
  2550. const C_DATATYPE_SPECIAL_DATETIME = 81;
  2551. /**
  2552. *
  2553. * DATA TYPE
  2554. * Specified. This means the developer or DBA
  2555. * has altered the column to a different type not
  2556. * recognized by RedBean. This high number makes sure
  2557. * it will not be converted back to another type by accident.
  2558. * @var integer
  2559. */
  2560. const C_DATATYPE_SPECIFIED = 99;
  2561. /**
  2562. * Spatial types
  2563. * @var integer
  2564. */
  2565. const C_DATATYPE_SPECIAL_POINT = 100;
  2566. const C_DATATYPE_SPECIAL_LINESTRING = 101;
  2567. const C_DATATYPE_SPECIAL_GEOMETRY = 102;
  2568. const C_DATATYPE_SPECIAL_POLYGON = 103;
  2569. const C_DATATYPE_SPECIAL_MULTIPOINT = 104;
  2570. const C_DATATYPE_SPECIAL_MULTIPOLYGON = 105;
  2571. const C_DATATYPE_SPECIAL_GEOMETRYCOLLECTION = 106;
  2572. /**
  2573. * Holds the RedBean Database Adapter.
  2574. * @var RedBean_Adapter_DBAdapter
  2575. */
  2576. protected $adapter;
  2577. /**
  2578. * character to escape keyword table/column names
  2579. * @var string
  2580. */
  2581. protected $quoteCharacter = '`';
  2582. /**
  2583. * Constructor.
  2584. * The Query Writer Constructor also sets up the database.
  2585. *
  2586. * @param RedBean_Adapter_DBAdapter $adapter adapter
  2587. *
  2588. */
  2589. public function __construct( RedBean_Adapter $adapter ) {
  2590. $this->typeno_sqltype = array(
  2591. RedBean_QueryWriter_MySQL::C_DATATYPE_BOOL=>" TINYINT(1) ",
  2592. RedBean_QueryWriter_MySQL::C_DATATYPE_UINT8=>' TINYINT(3) UNSIGNED ',
  2593. RedBean_QueryWriter_MySQL::C_DATATYPE_UINT32=>' INT(11) UNSIGNED ',
  2594. RedBean_QueryWriter_MySQL::C_DATATYPE_DOUBLE=>' DOUBLE ',
  2595. RedBean_QueryWriter_MySQL::C_DATATYPE_TEXT8=>' VARCHAR(255) ',
  2596. RedBean_QueryWriter_MySQL::C_DATATYPE_TEXT16=>' TEXT ',
  2597. RedBean_QueryWriter_MySQL::C_DATATYPE_TEXT32=>' LONGTEXT ',
  2598. RedBean_QueryWriter_MySQL::C_DATATYPE_SPECIAL_DATE=>' DATE ',
  2599. RedBean_QueryWriter_MySQL::C_DATATYPE_SPECIAL_DATETIME=>' DATETIME ',
  2600. RedBean_QueryWriter_MySQL::C_DATATYPE_SPECIAL_POINT=>' POINT ',
  2601. RedBean_QueryWriter_MySQL::C_DATATYPE_SPECIAL_LINESTRING=>' LINESTRING ',
  2602. RedBean_QueryWriter_MySQL::C_DATATYPE_SPECIAL_GEOMETRY=>' GEOMETRY ',
  2603. RedBean_QueryWriter_MySQL::C_DATATYPE_SPECIAL_POLYGON=>' POLYGON ',
  2604. RedBean_QueryWriter_MySQL::C_DATATYPE_SPECIAL_MULTIPOINT=>' MULTIPOINT ',
  2605. RedBean_QueryWriter_MySQL::C_DATATYPE_SPECIAL_MULTIPOLYGON=>' MULTIPOLYGON ',
  2606. RedBean_QueryWriter_MySQL::C_DATATYPE_SPECIAL_GEOMETRYCOLLECTION=>' GEOMETRYCOLLECTION ',
  2607. );
  2608. $this->sqltype_typeno = array();
  2609. foreach($this->typeno_sqltype as $k=>$v)
  2610. $this->sqltype_typeno[trim(strtolower($v))]=$k;
  2611. $this->adapter = $adapter;
  2612. parent::__construct();
  2613. }
  2614. /**
  2615. * This method returns the datatype to be used for primary key IDS and
  2616. * foreign keys. Returns one if the data type constants.
  2617. *
  2618. * @return integer $const data type to be used for IDS.
  2619. */
  2620. public function getTypeForID() {
  2621. return static::C_DATATYPE_UINT32;
  2622. }
  2623. /**
  2624. * Returns all tables in the database.
  2625. *
  2626. * @return array $tables tables
  2627. */
  2628. public function getTables() {
  2629. return $this->adapter->getCol( 'show tables' );
  2630. }
  2631. /**
  2632. * Creates an empty, column-less table for a bean based on it's type.
  2633. * This function creates an empty table for a bean. It uses the
  2634. * safeTable() function to convert the type name to a table name.
  2635. *
  2636. * @param string $table type of bean you want to create a table for
  2637. *
  2638. * @return void
  2639. */
  2640. public function createTable( $table ) {
  2641. $table = $this->safeTable($table);
  2642. $sql = " CREATE TABLE $table (
  2643. id INT( 11 ) UNSIGNED NOT NULL AUTO_INCREMENT ,
  2644. PRIMARY KEY ( id )
  2645. ) ENGINE = InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci ";
  2646. $this->adapter->exec( $sql );
  2647. }
  2648. /**
  2649. * Returns an array containing the column names of the specified table.
  2650. *
  2651. * @param string $table table
  2652. *
  2653. * @return array $columns columns
  2654. */
  2655. public function getColumns( $table ) {
  2656. $table = $this->safeTable($table);
  2657. $columnsRaw = $this->adapter->get("DESCRIBE $table");
  2658. foreach($columnsRaw as $r) {
  2659. $columns[$r['Field']]=$r['Type'];
  2660. }
  2661. return $columns;
  2662. }
  2663. /**
  2664. * Returns the MySQL Column Type Code (integer) that corresponds
  2665. * to the given value type.
  2666. *
  2667. * @param string $value value
  2668. *
  2669. * @return integer $type type
  2670. */
  2671. public function scanType( $value, $flagSpecial=false ) {
  2672. $this->svalue = $value;
  2673. if (is_null($value)) {
  2674. return RedBean_QueryWriter_MySQL::C_DATATYPE_BOOL;
  2675. }
  2676. if ($flagSpecial) {
  2677. if (strpos($value,'POINT(')===0) {
  2678. $this->svalue = $this->adapter->getCell('SELECT GeomFromText(?)',array($value));
  2679. return RedBean_QueryWriter_MySQL::C_DATATYPE_SPECIAL_POINT;
  2680. }
  2681. if (strpos($value,'LINESTRING(')===0) {
  2682. $this->svalue = $this->adapter->getCell('SELECT GeomFromText(?)',array($value));
  2683. return RedBean_QueryWriter_MySQL::C_DATATYPE_SPECIAL_LINESTRING;
  2684. }
  2685. if (strpos($value,'POLYGON(')===0) {
  2686. $this->svalue = $this->adapter->getCell('SELECT GeomFromText(?)',array($value));
  2687. return RedBean_QueryWriter_MySQL::C_DATATYPE_SPECIAL_POLYGON;
  2688. }
  2689. if (strpos($value,'MULTIPOINT(')===0) {
  2690. $this->svalue = $this->adapter->getCell('SELECT GeomFromText(?)',array($value));
  2691. return RedBean_QueryWriter_MySQL::C_DATATYPE_SPECIAL_MULTIPOINT;
  2692. }
  2693. if (preg_match('/^\d{4}\-\d\d-\d\d$/',$value)) {
  2694. return RedBean_QueryWriter_MySQL::C_DATATYPE_SPECIAL_DATE;
  2695. }
  2696. if (preg_match('/^\d{4}\-\d\d-\d\d\s\d\d:\d\d:\d\d$/',$value)) {
  2697. return RedBean_QueryWriter_MySQL::C_DATATYPE_SPECIAL_DATETIME;
  2698. }
  2699. }
  2700. $value = strval($value);
  2701. if (!$this->startsWithZeros($value)) {
  2702. if ($value=='1' || $value=='') {
  2703. return RedBean_QueryWriter_MySQL::C_DATATYPE_BOOL;
  2704. }
  2705. if (is_numeric($value) && (floor($value)==$value) && $value >= 0 && $value <= 255 ) {
  2706. return RedBean_QueryWriter_MySQL::C_DATATYPE_UINT8;
  2707. }
  2708. if (is_numeric($value) && (floor($value)==$value) && $value >= 0 && $value <= 4294967295 ) {
  2709. return RedBean_QueryWriter_MySQL::C_DATATYPE_UINT32;
  2710. }
  2711. if (is_numeric($value)) {
  2712. return RedBean_QueryWriter_MySQL::C_DATATYPE_DOUBLE;
  2713. }
  2714. }
  2715. if (strlen($value) <= 255) {
  2716. return RedBean_QueryWriter_MySQL::C_DATATYPE_TEXT8;
  2717. }
  2718. if (strlen($value) <= 65535) {
  2719. return RedBean_QueryWriter_MySQL::C_DATATYPE_TEXT16;
  2720. }
  2721. return RedBean_QueryWriter_MySQL::C_DATATYPE_TEXT32;
  2722. }
  2723. /**
  2724. * Returns the Type Code for a Column Description.
  2725. * Given an SQL column description this method will return the corresponding
  2726. * code for the writer. If the include specials flag is set it will also
  2727. * return codes for special columns. Otherwise special columns will be identified
  2728. * as specified columns.
  2729. *
  2730. * @param string $typedescription description
  2731. * @param boolean $includeSpecials whether you want to get codes for special columns as well
  2732. *
  2733. * @return integer $typecode code
  2734. */
  2735. public function code( $typedescription, $includeSpecials = false ) {
  2736. $r = ((isset($this->sqltype_typeno[$typedescription])) ? $this->sqltype_typeno[$typedescription] : static::C_DATATYPE_SPECIFIED);
  2737. if ($includeSpecials) return $r;
  2738. if ($r > static::C_DATATYPE_SPECIFIED) return static::C_DATATYPE_SPECIFIED;
  2739. return $r;
  2740. }
  2741. /**
  2742. * This method upgrades the column to the specified data type.
  2743. * This methods accepts a type and infers the corresponding table name.
  2744. *
  2745. * @param string $type type / table that needs to be adjusted
  2746. * @param string $column column that needs to be altered
  2747. * @param integer $datatype target data type
  2748. *
  2749. * @return void
  2750. */
  2751. public function widenColumn( $type, $column, $datatype ) {
  2752. $table = $type;
  2753. $type = $datatype;
  2754. $table = $this->safeTable($table);
  2755. $column = $this->safeColumn($column);
  2756. $newtype = array_key_exists($type, $this->typeno_sqltype) ? $this->typeno_sqltype[$type] : '';
  2757. $changecolumnSQL = "ALTER TABLE $table CHANGE $column $column $newtype ";
  2758. $this->adapter->exec( $changecolumnSQL );
  2759. }
  2760. /**
  2761. * Adds a Unique index constrain to the table.
  2762. *
  2763. * @param string $table table
  2764. * @param string $col1 column
  2765. * @param string $col2 column
  2766. *
  2767. * @return void
  2768. */
  2769. public function addUniqueIndex( $table,$columns ) {
  2770. $table = $this->safeTable($table);
  2771. sort($columns); //else we get multiple indexes due to order-effects
  2772. foreach($columns as $k=>$v) {
  2773. $columns[$k]= $this->safeColumn($v);
  2774. }
  2775. $r = $this->adapter->get("SHOW INDEX FROM $table");
  2776. $name = 'UQ_'.sha1(implode(',',$columns));
  2777. if ($r) {
  2778. foreach($r as $i) {
  2779. if ($i['Key_name']== $name) {
  2780. return;
  2781. }
  2782. }
  2783. }
  2784. $sql = "ALTER IGNORE TABLE $table
  2785. ADD UNIQUE INDEX $name (".implode(',',$columns).")";
  2786. $this->adapter->exec($sql);
  2787. }
  2788. /**
  2789. * Tests whether a given SQL state is in the list of states.
  2790. *
  2791. * @param string $state code
  2792. * @param array $list array of sql states
  2793. *
  2794. * @return boolean $yesno occurs in list
  2795. */
  2796. public function sqlStateIn($state, $list) {
  2797. $stateMap = array(
  2798. '42S02'=>RedBean_QueryWriter::C_SQLSTATE_NO_SUCH_TABLE,
  2799. '42S22'=>RedBean_QueryWriter::C_SQLSTATE_NO_SUCH_COLUMN,
  2800. '23000'=>RedBean_QueryWriter::C_SQLSTATE_INTEGRITY_CONSTRAINT_VIOLATION
  2801. );
  2802. return in_array((isset($stateMap[$state]) ? $stateMap[$state] : '0'),$list);
  2803. }
  2804. /**
  2805. * Add the constraints for a specific database driver: MySQL.
  2806. * @todo Too many arguments; find a way to solve this in a neater way.
  2807. *
  2808. * @param string $table table
  2809. * @param string $table1 table1
  2810. * @param string $table2 table2
  2811. * @param string $property1 property1
  2812. * @param string $property2 property2
  2813. *
  2814. * @return boolean $succes whether the constraint has been applied
  2815. */
  2816. protected function constrain($table, $table1, $table2, $property1, $property2) {
  2817. return true;
  2818. try{
  2819. $db = $this->adapter->getCell('select database()');
  2820. $fks = $this->adapter->getCell("
  2821. SELECT count(*)
  2822. FROM information_schema.KEY_COLUMN_USAGE
  2823. WHERE TABLE_SCHEMA = ? AND TABLE_NAME = ? AND
  2824. CONSTRAINT_NAME <>'PRIMARY' AND REFERENCED_TABLE_NAME is not null
  2825. ",array($db,$table));
  2826. //already foreign keys added in this association table
  2827. if ($fks>0) return false;
  2828. $columns = $this->getColumns($table);
  2829. if ($this->code($columns[$property1])!==RedBean_QueryWriter_MySQL::C_DATATYPE_UINT32) {
  2830. $this->widenColumn($table, $property1, RedBean_QueryWriter_MySQL::C_DATATYPE_UINT32);
  2831. }
  2832. if ($this->code($columns[$property2])!==RedBean_QueryWriter_MySQL::C_DATATYPE_UINT32) {
  2833. $this->widenColumn($table, $property2, RedBean_QueryWriter_MySQL::C_DATATYPE_UINT32);
  2834. }
  2835. $sql = "
  2836. ALTER TABLE ".$this->noKW($table)."
  2837. ADD FOREIGN KEY($property1) references `$table1`(id) ON DELETE CASCADE;
  2838. ";
  2839. $this->adapter->exec( $sql );
  2840. $sql ="
  2841. ALTER TABLE ".$this->noKW($table)."
  2842. ADD FOREIGN KEY($property2) references `$table2`(id) ON DELETE CASCADE
  2843. ";
  2844. $this->adapter->exec( $sql );
  2845. return true;
  2846. } catch(Exception $e){ return false; }
  2847. }
  2848. /**
  2849. * Drops all tables in database
  2850. */
  2851. public function wipeAll() {
  2852. $this->adapter->exec('SET FOREIGN_KEY_CHECKS=0;');
  2853. foreach($this->getTables() as $t) {
  2854. try{
  2855. $this->adapter->exec("drop table if exists`$t`");
  2856. }
  2857. catch(Exception $e){}
  2858. try{
  2859. $this->adapter->exec("drop view if exists`$t`");
  2860. }
  2861. catch(Exception $e){}
  2862. }
  2863. $this->adapter->exec('SET FOREIGN_KEY_CHECKS=1;');
  2864. }
  2865. }
  2866. /**
  2867. * RedBean SQLiteWriter with support for SQLite types
  2868. *
  2869. * @file RedBean/QueryWriter/SQLiteT.php
  2870. * @description Represents a SQLite Database to RedBean
  2871. * To write a driver for a different database for RedBean
  2872. * you should only have to change this file.
  2873. * @author Gabor de Mooij and the RedBeanPHP Community
  2874. * @license BSD/GPLv2
  2875. *
  2876. * (c) copyright G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community.
  2877. * This source file is subject to the BSD/GPLv2 License that is bundled
  2878. * with this source code in the file license.txt.
  2879. */
  2880. class RedBean_QueryWriter_SQLiteT extends RedBean_QueryWriter_AQueryWriter implements RedBean_QueryWriter {
  2881. /**
  2882. *
  2883. * @var RedBean_Adapter_DBAdapter
  2884. * Holds database adapter
  2885. */
  2886. protected $adapter;
  2887. /**
  2888. * @var string
  2889. * character to escape keyword table/column names
  2890. */
  2891. protected $quoteCharacter = '`';
  2892. /**
  2893. * Here we describe the datatypes that RedBean
  2894. * Uses internally. If you write a QueryWriter for
  2895. * RedBean you should provide a list of types like this.
  2896. */
  2897. /**
  2898. * DATA TYPE
  2899. * Integer Data type
  2900. * @var integer
  2901. */
  2902. const C_DATATYPE_INTEGER = 0;
  2903. /**
  2904. * DATA TYPE
  2905. * Numeric Data type (for REAL and date/time)
  2906. * @var integer
  2907. */
  2908. const C_DATATYPE_NUMERIC = 1;
  2909. /**
  2910. * DATA TYPE
  2911. * Text type
  2912. * @var integer
  2913. */
  2914. const C_DATATYPE_TEXT = 2;
  2915. /**
  2916. * DATA TYPE
  2917. * Specified. This means the developer or DBA
  2918. * has altered the column to a different type not
  2919. * recognized by RedBean. This high number makes sure
  2920. * it will not be converted back to another type by accident.
  2921. * @var integer
  2922. */
  2923. const C_DATATYPE_SPECIFIED = 99;
  2924. /**
  2925. * Constructor
  2926. * The Query Writer Constructor also sets up the database
  2927. *
  2928. * @param RedBean_Adapter_DBAdapter $adapter adapter
  2929. */
  2930. public function __construct( RedBean_Adapter $adapter ) {
  2931. $this->typeno_sqltype = array(
  2932. RedBean_QueryWriter_SQLiteT::C_DATATYPE_INTEGER=>'INTEGER',
  2933. RedBean_QueryWriter_SQLiteT::C_DATATYPE_NUMERIC=>'NUMERIC',
  2934. RedBean_QueryWriter_SQLiteT::C_DATATYPE_TEXT=>'TEXT',
  2935. );
  2936. $this->sqltype_typeno = array();
  2937. foreach($this->typeno_sqltype as $k=>$v)
  2938. $this->sqltype_typeno[$v]=$k;
  2939. $this->adapter = $adapter;
  2940. parent::__construct($adapter);
  2941. }
  2942. /**
  2943. * This method returns the datatype to be used for primary key IDS and
  2944. * foreign keys. Returns one if the data type constants.
  2945. *
  2946. * @return integer $const data type to be used for IDS.
  2947. */
  2948. public function getTypeForID() {
  2949. return static::C_DATATYPE_INTEGER;
  2950. }
  2951. /**
  2952. * Returns the MySQL Column Type Code (integer) that corresponds
  2953. * to the given value type.
  2954. *
  2955. * @param string $value value
  2956. *
  2957. * @return integer $type type
  2958. */
  2959. public function scanType( $value, $flagSpecial=false ) {
  2960. $this->svalue=$value;
  2961. if ($value===false) return static::C_DATATYPE_INTEGER;
  2962. if ($value===null) return static::C_DATATYPE_INTEGER; //for fks
  2963. if ($this->startsWithZeros($value)) return static::C_DATATYPE_TEXT;
  2964. if (is_numeric($value) && (intval($value)==$value) && $value<2147483648) return static::C_DATATYPE_INTEGER;
  2965. if ((is_numeric($value) && $value < 2147483648)
  2966. || preg_match('/\d{4}\-\d\d\-\d\d/',$value)
  2967. || preg_match('/\d{4}\-\d\d\-\d\d\s\d\d:\d\d:\d\d/',$value)
  2968. ) {
  2969. return static::C_DATATYPE_NUMERIC;
  2970. }
  2971. return static::C_DATATYPE_TEXT;
  2972. }
  2973. /**
  2974. * Adds a column of a given type to a table
  2975. *
  2976. * @param string $table table
  2977. * @param string $column column
  2978. * @param integer $type type
  2979. */
  2980. public function addColumn( $table, $column, $type) {
  2981. $column = $this->check($column);
  2982. $table = $this->check($table);
  2983. $type=$this->typeno_sqltype[$type];
  2984. $sql = "ALTER TABLE `$table` ADD `$column` $type ";
  2985. $this->adapter->exec( $sql );
  2986. }
  2987. /**
  2988. * Returns the Type Code for a Column Description.
  2989. * Given an SQL column description this method will return the corresponding
  2990. * code for the writer. If the include specials flag is set it will also
  2991. * return codes for special columns. Otherwise special columns will be identified
  2992. * as specified columns.
  2993. *
  2994. * @param string $typedescription description
  2995. * @param boolean $includeSpecials whether you want to get codes for special columns as well
  2996. *
  2997. * @return integer $typecode code
  2998. */
  2999. public function code( $typedescription, $includeSpecials = false ) {
  3000. $r = ((isset($this->sqltype_typeno[$typedescription])) ? $this->sqltype_typeno[$typedescription] : 99);
  3001. if ($includeSpecials) return $r;
  3002. if ($r > static::C_DATATYPE_SPECIFIED) return static::C_DATATYPE_SPECIFIED;
  3003. return $r;
  3004. }
  3005. /**
  3006. * Quote Items, to prevent issues with reserved words.
  3007. *
  3008. * @param array $items items to quote
  3009. *
  3010. * @return $quotedfItems quoted items
  3011. */
  3012. private function quote( $items ) {
  3013. foreach($items as $k=>$item) {
  3014. $items[$k]=$this->noKW($item);
  3015. }
  3016. return $items;
  3017. }
  3018. /**
  3019. * This method upgrades the column to the specified data type.
  3020. * This methods accepts a type and infers the corresponding table name.
  3021. *
  3022. * @param string $type type / table that needs to be adjusted
  3023. * @param string $column column that needs to be altered
  3024. * @param integer $datatype target data type
  3025. *
  3026. * @return void
  3027. */
  3028. public function widenColumn( $type, $column, $datatype ) {
  3029. $table = $this->safeTable($type,true);
  3030. $column = $this->safeColumn($column,true);
  3031. $newtype = $this->typeno_sqltype[$datatype];
  3032. $oldColumns = $this->getColumns($type);
  3033. $oldColumnNames = $this->quote(array_keys($oldColumns));
  3034. $newTableDefStr='';
  3035. foreach($oldColumns as $oldName=>$oldType) {
  3036. if ($oldName != 'id') {
  3037. if ($oldName!=$column) {
  3038. $newTableDefStr .= ",`$oldName` $oldType";
  3039. }
  3040. else {
  3041. $newTableDefStr .= ",`$oldName` $newtype";
  3042. }
  3043. }
  3044. }
  3045. $q = array();
  3046. $q[] = "DROP TABLE IF EXISTS tmp_backup;";
  3047. $q[] = "CREATE TEMPORARY TABLE tmp_backup(".implode(",",$oldColumnNames).");";
  3048. $q[] = "INSERT INTO tmp_backup SELECT * FROM `$table`;";
  3049. $q[] = "DROP TABLE `$table`;";
  3050. $q[] = "CREATE TABLE `$table` ( `id` INTEGER PRIMARY KEY AUTOINCREMENT $newTableDefStr );";
  3051. $q[] = "INSERT INTO `$table` SELECT * FROM tmp_backup;";
  3052. $q[] = "DROP TABLE tmp_backup;";
  3053. foreach($q as $sq) {
  3054. $this->adapter->exec($sq);
  3055. }
  3056. }
  3057. /**
  3058. * Returns all tables in the database
  3059. *
  3060. * @return array $tables tables
  3061. */
  3062. public function getTables() {
  3063. return $this->adapter->getCol( "SELECT name FROM sqlite_master
  3064. WHERE type='table' AND name!='sqlite_sequence';" );
  3065. }
  3066. /**
  3067. * Creates an empty, column-less table for a bean.
  3068. *
  3069. * @param string $table table
  3070. */
  3071. public function createTable( $table ) {
  3072. $table = $this->safeTable($table);
  3073. $sql = "CREATE TABLE $table ( id INTEGER PRIMARY KEY AUTOINCREMENT ) ";
  3074. $this->adapter->exec( $sql );
  3075. }
  3076. /**
  3077. * Returns an array containing the column names of the specified table.
  3078. *
  3079. * @param string $table table
  3080. *
  3081. * @return array $columns columns
  3082. */
  3083. public function getColumns( $table ) {
  3084. $table = $this->safeTable($table, true);
  3085. $columnsRaw = $this->adapter->get("PRAGMA table_info('$table')");
  3086. $columns = array();
  3087. foreach($columnsRaw as $r) {
  3088. $columns[$r['name']]=$r['type'];
  3089. }
  3090. return $columns;
  3091. }
  3092. /**
  3093. * Adds a Unique index constrain to the table.
  3094. *
  3095. * @param string $table table
  3096. * @param string $column1 first column
  3097. * @param string $column2 second column
  3098. *
  3099. * @return void
  3100. */
  3101. public function addUniqueIndex( $table,$columns ) {
  3102. $table = $this->safeTable($table);
  3103. $name = 'UQ_'.sha1(implode(',',$columns));
  3104. $sql = "CREATE UNIQUE INDEX IF NOT EXISTS $name ON $table (".implode(',',$columns).")";
  3105. $this->adapter->exec($sql);
  3106. }
  3107. /**
  3108. * Given an Database Specific SQLState and a list of QueryWriter
  3109. * Standard SQL States this function converts the raw SQL state to a
  3110. * database agnostic ANSI-92 SQL states and checks if the given state
  3111. * is in the list of agnostic states.
  3112. *
  3113. * @param string $state state
  3114. * @param array $list list of states
  3115. *
  3116. * @return boolean $isInArray whether state is in list
  3117. */
  3118. public function sqlStateIn($state, $list) {
  3119. $stateMap = array(
  3120. 'HY000'=>RedBean_QueryWriter::C_SQLSTATE_NO_SUCH_TABLE,
  3121. '23000'=>RedBean_QueryWriter::C_SQLSTATE_INTEGRITY_CONSTRAINT_VIOLATION
  3122. );
  3123. return in_array((isset($stateMap[$state]) ? $stateMap[$state] : '0'),$list);
  3124. }
  3125. /**
  3126. * Counts rows in a table.
  3127. * Uses SQLite optimization for deleting all records (i.e. no WHERE)
  3128. *
  3129. * @param string $beanType
  3130. *
  3131. * @return integer $numRowsFound
  3132. */
  3133. public function wipe($type) {
  3134. $table = $this->safeTable($type);
  3135. $this->adapter->exec("DELETE FROM $table");
  3136. }
  3137. /**
  3138. * Adds a foreign key to a type
  3139. *
  3140. * @param string $type type you want to modify table of
  3141. * @param string $targetType target type
  3142. * @param string $field field of the type that needs to get the fk
  3143. * @param string $targetField field where the fk needs to point to
  3144. * @param boolean $isDep whether this field is dependent on it's referenced record
  3145. *
  3146. * @return bool $success whether an FK has been added
  3147. */
  3148. public function addFK( $type, $targetType, $field, $targetField, $isDep=false) {
  3149. return $this->buildFK($type, $targetType, $field, $targetField, $isDep);
  3150. }
  3151. /**
  3152. * Adds a foreign key to a type
  3153. *
  3154. * @param string $type type you want to modify table of
  3155. * @param string $targetType target type
  3156. * @param string $field field of the type that needs to get the fk
  3157. * @param string $targetField field where the fk needs to point to
  3158. * @param integer $buildopt 0 = NO ACTION, 1 = ON DELETE CASCADE
  3159. *
  3160. * @return bool $success whether an FK has been added
  3161. */
  3162. protected function buildFK($type, $targetType, $field, $targetField,$constraint=false) {
  3163. try{
  3164. $consSQL = ($constraint ? 'CASCADE' : 'SET NULL');
  3165. $table = $this->safeTable($type,true);
  3166. $targetTable = $this->safeTable($targetType,true);
  3167. $field = $this->safeColumn($field,true);
  3168. $targetField = $this->safeColumn($targetField,true);
  3169. $oldColumns = $this->getColumns($type);
  3170. $oldColumnNames = $this->quote(array_keys($oldColumns));
  3171. $newTableDefStr='';
  3172. foreach($oldColumns as $oldName=>$oldType) {
  3173. if ($oldName != 'id') {
  3174. $newTableDefStr .= ",`$oldName` $oldType";
  3175. }
  3176. }
  3177. //retrieve old foreign keys
  3178. $sqlGetOldFKS = "PRAGMA foreign_key_list('$table'); ";
  3179. $oldFKs = $this->adapter->get($sqlGetOldFKS);
  3180. $restoreFKSQLSnippets = "";
  3181. foreach($oldFKs as $oldFKInfo) {
  3182. if ($oldFKInfo['from']==$field && $oldFKInfo['on_delete']==$consSQL) {
  3183. //this field already has a FK.
  3184. return false;
  3185. }
  3186. if ($oldFKInfo['from']==$field && $oldFKInfo['on_delete']!=$consSQL) {
  3187. //this field already has a FK.but needs to be replaced
  3188. continue;
  3189. }
  3190. $oldTable = $table;
  3191. $oldField = $oldFKInfo['from'];
  3192. $oldTargetTable = $oldFKInfo['table'];
  3193. $oldTargetField = $oldFKInfo['to'];
  3194. $restoreFKSQLSnippets .= ", FOREIGN KEY(`$oldField`) REFERENCES `$oldTargetTable`(`$oldTargetField`) ON DELETE ".$oldFKInfo['on_delete'];
  3195. }
  3196. $fkDef = $restoreFKSQLSnippets;
  3197. if ($constraint) {
  3198. $fkDef .= ", FOREIGN KEY(`$field`) REFERENCES `$targetTable`(`$targetField`) ON DELETE CASCADE ";
  3199. }
  3200. else {
  3201. $fkDef .= ", FOREIGN KEY(`$field`) REFERENCES `$targetTable`(`$targetField`) ON DELETE SET NULL ON UPDATE SET NULL";
  3202. }
  3203. $q = array();
  3204. $q[] = "DROP TABLE IF EXISTS tmp_backup;";
  3205. $q[] = "CREATE TEMPORARY TABLE tmp_backup(".implode(',',$oldColumnNames).");";
  3206. $q[] = "INSERT INTO tmp_backup SELECT * FROM `$table`;";
  3207. $q[] = "PRAGMA foreign_keys = 0 ";
  3208. $q[] = "DROP TABLE `$table`;";
  3209. $q[] = "CREATE TABLE `$table` ( `id` INTEGER PRIMARY KEY AUTOINCREMENT $newTableDefStr $fkDef );";
  3210. $q[] = "INSERT INTO `$table` SELECT * FROM tmp_backup;";
  3211. $q[] = "DROP TABLE tmp_backup;";
  3212. $q[] = "PRAGMA foreign_keys = 1 ";
  3213. foreach($q as $sq) {
  3214. $this->adapter->exec($sq);
  3215. }
  3216. return true;
  3217. }
  3218. catch(Exception $e){ return false; }
  3219. }
  3220. /**
  3221. * Add the constraints for a specific database driver: SQLite.
  3222. * @todo Too many arguments; find a way to solve this in a neater way.
  3223. *
  3224. * @param string $table table
  3225. * @param string $table1 table1
  3226. * @param string $table2 table2
  3227. * @param string $property1 property1
  3228. * @param string $property2 property2
  3229. *
  3230. * @return boolean $succes whether the constraint has been applied
  3231. */
  3232. protected function constrain($table, $table1, $table2, $property1, $property2) {
  3233. $writer = $this;
  3234. $adapter = $this->adapter;
  3235. $firstState = $this->buildFK($table,$table1,$property1,'id',true);
  3236. $secondState = $this->buildFK($table,$table2,$property2,'id',true);
  3237. return ($firstState && $secondState);
  3238. }
  3239. /**
  3240. * Removes all tables and views from the database.
  3241. *
  3242. * @return void
  3243. */
  3244. public function wipeAll() {
  3245. $this->adapter->exec('PRAGMA foreign_keys = 0 ');
  3246. foreach($this->getTables() as $t) {
  3247. try{
  3248. $this->adapter->exec("drop table if exists`$t`");
  3249. }
  3250. catch(Exception $e){}
  3251. try{
  3252. $this->adapter->exec("drop view if exists`$t`");
  3253. }
  3254. catch(Exception $e){}
  3255. }
  3256. $this->adapter->exec('PRAGMA foreign_keys = 1 ');
  3257. }
  3258. }
  3259. /**
  3260. * RedBean PostgreSQL Query Writer
  3261. *
  3262. * @file RedBean/QueryWriter/PostgreSQL.php
  3263. * @description QueryWriter for the PostgreSQL database system.
  3264. * @author Gabor de Mooij and the RedBeanPHP Community
  3265. * @license BSD/GPLv2
  3266. *
  3267. * (c) copyright G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community.
  3268. * This source file is subject to the BSD/GPLv2 License that is bundled
  3269. * with this source code in the file license.txt.
  3270. */
  3271. class RedBean_QueryWriter_PostgreSQL extends RedBean_QueryWriter_AQueryWriter implements RedBean_QueryWriter {
  3272. /**
  3273. * DATA TYPE
  3274. * Integer Data Type
  3275. * @var integer
  3276. */
  3277. const C_DATATYPE_INTEGER = 0;
  3278. /**
  3279. * DATA TYPE
  3280. * Double Precision Type
  3281. * @var integer
  3282. */
  3283. const C_DATATYPE_DOUBLE = 1;
  3284. /**
  3285. * DATA TYPE
  3286. * String Data Type
  3287. * @var integer
  3288. */
  3289. const C_DATATYPE_TEXT = 3;
  3290. /**
  3291. * Special type date for storing date values: YYYY-MM-DD
  3292. * @var integer
  3293. */
  3294. const C_DATATYPE_SPECIAL_DATE = 80;
  3295. /**
  3296. * Special type date for storing date values: YYYY-MM-DD HH:MM:SS
  3297. * @var integer
  3298. */
  3299. const C_DATATYPE_SPECIAL_DATETIME = 81;
  3300. const C_DATATYPE_SPECIAL_POINT = 101;
  3301. const C_DATATYPE_SPECIAL_LINE = 102;
  3302. const C_DATATYPE_SPECIAL_LSEG = 103;
  3303. const C_DATATYPE_SPECIAL_BOX = 104;
  3304. const C_DATATYPE_SPECIAL_CIRCLE = 105;
  3305. const C_DATATYPE_SPECIAL_POLYGON = 106;
  3306. /**
  3307. * Specified field type cannot be overruled
  3308. * @var integer
  3309. */
  3310. const C_DATATYPE_SPECIFIED = 99;
  3311. /**
  3312. * Holds Database Adapter
  3313. * @var RedBean_DBAdapter
  3314. */
  3315. protected $adapter;
  3316. /**
  3317. * character to escape keyword table/column names
  3318. * @var string
  3319. */
  3320. protected $quoteCharacter = '"';
  3321. /**
  3322. * Default Value
  3323. * @var string
  3324. */
  3325. protected $defaultValue = 'DEFAULT';
  3326. /**
  3327. * This method returns the datatype to be used for primary key IDS and
  3328. * foreign keys. Returns one if the data type constants.
  3329. *
  3330. * @return integer $const data type to be used for IDS.
  3331. */
  3332. public function getTypeForID() {
  3333. return static::C_DATATYPE_INTEGER;
  3334. }
  3335. /**
  3336. * Returns the insert suffix SQL Snippet
  3337. *
  3338. * @param string $table table
  3339. *
  3340. * @return string $sql SQL Snippet
  3341. */
  3342. protected function getInsertSuffix($table) {
  3343. return 'RETURNING id ';
  3344. }
  3345. /**
  3346. * Constructor
  3347. * The Query Writer Constructor also sets up the database
  3348. *
  3349. * @param RedBean_DBAdapter $adapter adapter
  3350. */
  3351. public function __construct( RedBean_Adapter_DBAdapter $adapter ) {
  3352. $this->typeno_sqltype = array(
  3353. static::C_DATATYPE_INTEGER=>' integer ',
  3354. static::C_DATATYPE_DOUBLE=>' double precision ',
  3355. static::C_DATATYPE_TEXT=>' text ',
  3356. static::C_DATATYPE_SPECIAL_DATE => ' date ',
  3357. static::C_DATATYPE_SPECIAL_DATETIME => ' timestamp without time zone ',
  3358. static::C_DATATYPE_SPECIAL_POINT => ' point ',
  3359. static::C_DATATYPE_SPECIAL_LINE => ' line ',
  3360. static::C_DATATYPE_SPECIAL_LSEG => ' lseg ',
  3361. static::C_DATATYPE_SPECIAL_BOX => ' box ',
  3362. static::C_DATATYPE_SPECIAL_CIRCLE => ' circle ',
  3363. static::C_DATATYPE_SPECIAL_POLYGON => ' polygon ',
  3364. );
  3365. $this->sqltype_typeno = array();
  3366. foreach($this->typeno_sqltype as $k=>$v)
  3367. $this->sqltype_typeno[trim(strtolower($v))]=$k;
  3368. $this->adapter = $adapter;
  3369. parent::__construct();
  3370. }
  3371. /**
  3372. * Returns all tables in the database
  3373. *
  3374. * @return array $tables tables
  3375. */
  3376. public function getTables() {
  3377. return $this->adapter->getCol( "select table_name from information_schema.tables
  3378. where table_schema = 'public'" );
  3379. }
  3380. /**
  3381. * Creates an empty, column-less table for a bean.
  3382. *
  3383. * @param string $table table to create
  3384. */
  3385. public function createTable( $table ) {
  3386. $table = $this->safeTable($table);
  3387. $sql = " CREATE TABLE $table (id SERIAL PRIMARY KEY); ";
  3388. $this->adapter->exec( $sql );
  3389. }
  3390. /**
  3391. * Returns an array containing the column names of the specified table.
  3392. *
  3393. * @param string $table table to get columns from
  3394. *
  3395. * @return array $columns array filled with column (name=>type)
  3396. */
  3397. public function getColumns( $table ) {
  3398. $table = $this->safeTable($table, true);
  3399. $columnsRaw = $this->adapter->get("select column_name, data_type from information_schema.columns where table_name='$table'");
  3400. foreach($columnsRaw as $r) {
  3401. $columns[$r['column_name']]=$r['data_type'];
  3402. }
  3403. return $columns;
  3404. }
  3405. /**
  3406. * Returns the pgSQL Column Type Code (integer) that corresponds
  3407. * to the given value type.
  3408. *
  3409. * @param string $value value to determine type of
  3410. *
  3411. * @return integer $type type code for this value
  3412. */
  3413. public function scanType( $value, $flagSpecial=false ) {
  3414. $this->svalue=$value;
  3415. if ($flagSpecial && $value) {
  3416. if (preg_match('/^\d{4}\-\d\d-\d\d$/',$value)) {
  3417. return RedBean_QueryWriter_PostgreSQL::C_DATATYPE_SPECIAL_DATE;
  3418. }
  3419. if (preg_match('/^\d{4}\-\d\d-\d\d\s\d\d:\d\d:\d\d(\.\d{1,6})?$/',$value)) {
  3420. return RedBean_QueryWriter_PostgreSQL::C_DATATYPE_SPECIAL_DATETIME;
  3421. }
  3422. if (strpos($value,'POINT(')===0) {
  3423. $this->svalue = str_replace('POINT','',$value);
  3424. return RedBean_QueryWriter_PostgreSQL::C_DATATYPE_SPECIAL_POINT;
  3425. }
  3426. if (strpos($value,'LSEG(')===0) {
  3427. $this->svalue = str_replace('LSEG','',$value);
  3428. return RedBean_QueryWriter_PostgreSQL::C_DATATYPE_SPECIAL_LSEG;
  3429. }
  3430. if (strpos($value,'BOX(')===0) {
  3431. $this->svalue = str_replace('BOX','',$value);
  3432. return RedBean_QueryWriter_PostgreSQL::C_DATATYPE_SPECIAL_BOX;
  3433. }
  3434. if (strpos($value,'CIRCLE(')===0) {
  3435. $this->svalue = str_replace('CIRCLE','',$value);
  3436. return RedBean_QueryWriter_PostgreSQL::C_DATATYPE_SPECIAL_CIRCLE;
  3437. }
  3438. if (strpos($value,'POLYGON(')===0) {
  3439. $this->svalue = str_replace('POLYGON','',$value);
  3440. return RedBean_QueryWriter_PostgreSQL::C_DATATYPE_SPECIAL_POLYGON;
  3441. }
  3442. }
  3443. $sz = ($this->startsWithZeros($value));
  3444. if ($sz) return static::C_DATATYPE_TEXT;
  3445. if ($value===null || ($value instanceof RedBean_Driver_PDO_NULL) ||(is_numeric($value)
  3446. && floor($value)==$value
  3447. && $value < 2147483648
  3448. && $value > -2147483648)) {
  3449. return static::C_DATATYPE_INTEGER;
  3450. }
  3451. elseif(is_numeric($value)) {
  3452. return static::C_DATATYPE_DOUBLE;
  3453. }
  3454. else {
  3455. return static::C_DATATYPE_TEXT;
  3456. }
  3457. }
  3458. /**
  3459. * Returns the Type Code for a Column Description.
  3460. * Given an SQL column description this method will return the corresponding
  3461. * code for the writer. If the include specials flag is set it will also
  3462. * return codes for special columns. Otherwise special columns will be identified
  3463. * as specified columns.
  3464. *
  3465. * @param string $typedescription description
  3466. * @param boolean $includeSpecials whether you want to get codes for special columns as well
  3467. *
  3468. * @return integer $typecode code
  3469. */
  3470. public function code( $typedescription, $includeSpecials = false ) {
  3471. $r = ((isset($this->sqltype_typeno[$typedescription])) ? $this->sqltype_typeno[$typedescription] : 99);
  3472. if ($includeSpecials) return $r;
  3473. if ($r > static::C_DATATYPE_SPECIFIED) return static::C_DATATYPE_SPECIFIED;
  3474. return $r;
  3475. }
  3476. /**
  3477. * This method upgrades the column to the specified data type.
  3478. * This methods accepts a type and infers the corresponding table name.
  3479. *
  3480. * @param string $type type / table that needs to be adjusted
  3481. * @param string $column column that needs to be altered
  3482. * @param integer $datatype target data type
  3483. *
  3484. * @return void
  3485. */
  3486. public function widenColumn( $type, $column, $datatype ) {
  3487. $table = $type;
  3488. $type = $datatype;
  3489. $table = $this->safeTable($table);
  3490. $column = $this->safeColumn($column);
  3491. $newtype = $this->typeno_sqltype[$type];
  3492. $changecolumnSQL = "ALTER TABLE $table \n\t ALTER COLUMN $column TYPE $newtype ";
  3493. $this->adapter->exec( $changecolumnSQL );
  3494. }
  3495. /**
  3496. * Adds a Unique index constrain to the table.
  3497. *
  3498. * @param string $table table to add index to
  3499. * @param string $col1 column to be part of index
  3500. * @param string $col2 column 2 to be part of index
  3501. *
  3502. * @return void
  3503. */
  3504. public function addUniqueIndex( $table,$columns ) {
  3505. $table = $this->safeTable($table, true);
  3506. sort($columns); //else we get multiple indexes due to order-effects
  3507. foreach($columns as $k=>$v) {
  3508. $columns[$k]=$this->safeColumn($v);
  3509. }
  3510. $r = $this->adapter->get("SELECT
  3511. i.relname as index_name
  3512. FROM
  3513. pg_class t,
  3514. pg_class i,
  3515. pg_index ix,
  3516. pg_attribute a
  3517. WHERE
  3518. t.oid = ix.indrelid
  3519. AND i.oid = ix.indexrelid
  3520. AND a.attrelid = t.oid
  3521. AND a.attnum = ANY(ix.indkey)
  3522. AND t.relkind = 'r'
  3523. AND t.relname = '$table'
  3524. ORDER BY t.relname, i.relname;");
  3525. $name = "UQ_".sha1($table.implode(',',$columns));
  3526. if ($r) {
  3527. foreach($r as $i) {
  3528. if (strtolower( $i['index_name'] )== strtolower( $name )) {
  3529. return;
  3530. }
  3531. }
  3532. }
  3533. $sql = "ALTER TABLE \"$table\"
  3534. ADD CONSTRAINT $name UNIQUE (".implode(',',$columns).")";
  3535. $this->adapter->exec($sql);
  3536. }
  3537. /**
  3538. * Given an Database Specific SQLState and a list of QueryWriter
  3539. * Standard SQL States this function converts the raw SQL state to a
  3540. * database agnostic ANSI-92 SQL states and checks if the given state
  3541. * is in the list of agnostic states.
  3542. *
  3543. * @param string $state state
  3544. * @param array $list list of states
  3545. *
  3546. * @return boolean $isInArray whether state is in list
  3547. */
  3548. public function sqlStateIn($state, $list) {
  3549. $stateMap = array(
  3550. '42P01'=>RedBean_QueryWriter::C_SQLSTATE_NO_SUCH_TABLE,
  3551. '42703'=>RedBean_QueryWriter::C_SQLSTATE_NO_SUCH_COLUMN,
  3552. '23505'=>RedBean_QueryWriter::C_SQLSTATE_INTEGRITY_CONSTRAINT_VIOLATION
  3553. );
  3554. return in_array((isset($stateMap[$state]) ? $stateMap[$state] : '0'),$list);
  3555. }
  3556. /**
  3557. * Adds a foreign key to a table. The foreign key will not have any action; you
  3558. * may configure this afterwards.
  3559. *
  3560. * @param string $type type you want to modify table of
  3561. * @param string $targetType target type
  3562. * @param string $field field of the type that needs to get the fk
  3563. * @param string $targetField field where the fk needs to point to
  3564. *
  3565. * @return bool $success whether an FK has been added
  3566. */
  3567. public function addFK( $type, $targetType, $field, $targetField, $isDep = false) {
  3568. try{
  3569. $table = $this->safeTable($type);
  3570. $column = $this->safeColumn($field);
  3571. $tableNoQ = $this->safeTable($type,true);
  3572. $columnNoQ = $this->safeColumn($field,true);
  3573. $targetTable = $this->safeTable($targetType);
  3574. $targetTableNoQ = $this->safeTable($targetType,true);
  3575. $targetColumn = $this->safeColumn($targetField);
  3576. $targetColumnNoQ = $this->safeColumn($targetField,true);
  3577. $sql = "SELECT
  3578. tc.constraint_name,
  3579. tc.table_name,
  3580. kcu.column_name,
  3581. ccu.table_name AS foreign_table_name,
  3582. ccu.column_name AS foreign_column_name,
  3583. rc.delete_rule
  3584. FROM
  3585. information_schema.table_constraints AS tc
  3586. JOIN information_schema.key_column_usage AS kcu ON tc.constraint_name = kcu.constraint_name
  3587. JOIN information_schema.constraint_column_usage AS ccu ON ccu.constraint_name = tc.constraint_name
  3588. JOIN information_schema.referential_constraints AS rc ON ccu.constraint_name = rc.constraint_name
  3589. WHERE constraint_type = 'FOREIGN KEY' AND tc.table_catalog=current_database()
  3590. AND tc.table_name = '$tableNoQ'
  3591. AND ccu.table_name = '$targetTableNoQ'
  3592. AND kcu.column_name = '$columnNoQ'
  3593. AND ccu.column_name = '$targetColumnNoQ'
  3594. ";
  3595. $row = $this->adapter->getRow($sql);
  3596. $flagAddKey = false;
  3597. if (!$row) $flagAddKey = true;
  3598. if ($row) {
  3599. if (($row['delete_rule']=='SET NULL' && $isDep) ||
  3600. ($row['delete_rule']!='SET NULL' && !$isDep)) {
  3601. //delete old key
  3602. $flagAddKey = true; //and order a new one
  3603. $cName = $row['constraint_name'];
  3604. $sql = "ALTER TABLE $table DROP CONSTRAINT $cName ";
  3605. $this->adapter->exec($sql);
  3606. }
  3607. }
  3608. if ($flagAddKey) {
  3609. $delRule = ($isDep ? 'CASCADE' : 'SET NULL');
  3610. $this->adapter->exec("ALTER TABLE $table
  3611. ADD FOREIGN KEY ( $column ) REFERENCES $targetTable (
  3612. $targetColumn) ON DELETE $delRule ON UPDATE SET NULL DEFERRABLE ;");
  3613. return true;
  3614. }
  3615. return false;
  3616. }
  3617. catch(Exception $e){ return false; }
  3618. }
  3619. /**
  3620. * Add the constraints for a specific database driver: PostgreSQL.
  3621. * @todo Too many arguments; find a way to solve this in a neater way.
  3622. *
  3623. * @param string $table table
  3624. * @param string $table1 table1
  3625. * @param string $table2 table2
  3626. * @param string $property1 property1
  3627. * @param string $property2 property2
  3628. *
  3629. * @return boolean $succes whether the constraint has been applied
  3630. */
  3631. protected function constrain($table, $table1, $table2, $property1, $property2) {
  3632. try{
  3633. $writer = $this;
  3634. $adapter = $this->adapter;
  3635. $fkCode = 'fk'.md5($table.$property1.$property2);
  3636. $sql = "
  3637. SELECT
  3638. c.oid,
  3639. n.nspname,
  3640. c.relname,
  3641. n2.nspname,
  3642. c2.relname,
  3643. cons.conname
  3644. FROM pg_class c
  3645. JOIN pg_namespace n ON n.oid = c.relnamespace
  3646. LEFT OUTER JOIN pg_constraint cons ON cons.conrelid = c.oid
  3647. LEFT OUTER JOIN pg_class c2 ON cons.confrelid = c2.oid
  3648. LEFT OUTER JOIN pg_namespace n2 ON n2.oid = c2.relnamespace
  3649. WHERE c.relkind = 'r'
  3650. AND n.nspname IN ('public')
  3651. AND (cons.contype = 'f' OR cons.contype IS NULL)
  3652. AND
  3653. ( cons.conname = '{$fkCode}a' OR cons.conname = '{$fkCode}b' )
  3654. ";
  3655. $rows = $adapter->get( $sql );
  3656. if (!count($rows)) {
  3657. $sql1 = "ALTER TABLE \"$table\" ADD CONSTRAINT
  3658. {$fkCode}a FOREIGN KEY ($property1)
  3659. REFERENCES \"$table1\" (id) ON DELETE CASCADE ";
  3660. $sql2 = "ALTER TABLE \"$table\" ADD CONSTRAINT
  3661. {$fkCode}b FOREIGN KEY ($property2)
  3662. REFERENCES \"$table2\" (id) ON DELETE CASCADE ";
  3663. $adapter->exec($sql1);
  3664. $adapter->exec($sql2);
  3665. }
  3666. return true;
  3667. }
  3668. catch(Exception $e){ return false; }
  3669. }
  3670. /**
  3671. * Removes all tables and views from the database.
  3672. */
  3673. public function wipeAll() {
  3674. $this->adapter->exec('SET CONSTRAINTS ALL DEFERRED');
  3675. foreach($this->getTables() as $t) {
  3676. $t = $this->noKW($t);
  3677. try{
  3678. $this->adapter->exec("drop table if exists $t CASCADE ");
  3679. }
  3680. catch(Exception $e){ }
  3681. try{
  3682. $this->adapter->exec("drop view if exists $t CASCADE ");
  3683. }
  3684. catch(Exception $e){ throw $e; }
  3685. }
  3686. $this->adapter->exec('SET CONSTRAINTS ALL IMMEDIATE');
  3687. }
  3688. }
  3689. /**
  3690. * RedBean CUBRID Writer
  3691. *
  3692. * @file RedBean/QueryWriter/CUBRID.php
  3693. * @description Represents a CUBRID Database to RedBean
  3694. * To write a driver for a different database for RedBean
  3695. * you should only have to change this file.
  3696. * @author Gabor de Mooij and the RedBeanPHP Community
  3697. * @license BSD/GPLv2
  3698. *
  3699. * (c) copyright G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community.
  3700. * This source file is subject to the BSD/GPLv2 License that is bundled
  3701. * with this source code in the file license.txt.
  3702. */
  3703. class RedBean_QueryWriter_CUBRID extends RedBean_QueryWriter_AQueryWriter implements RedBean_QueryWriter {
  3704. /**
  3705. * Here we describe the datatypes that RedBean
  3706. * Uses internally. If you write a QueryWriter for
  3707. * RedBean you should provide a list of types like this.
  3708. */
  3709. /**
  3710. *
  3711. * DATA TYPE
  3712. * Signed 4 byte Integer
  3713. * @var integer
  3714. */
  3715. const C_DATATYPE_INTEGER = 0;
  3716. /**
  3717. * DATA TYPE
  3718. * Double precision floating point number
  3719. * @var integer
  3720. */
  3721. const C_DATATYPE_DOUBLE = 1;
  3722. /**
  3723. *
  3724. * DATA TYPE
  3725. * Variable length text
  3726. * @var integer
  3727. */
  3728. const C_DATATYPE_STRING = 2;
  3729. /**
  3730. * Special type date for storing date values: YYYY-MM-DD
  3731. * @var integer
  3732. */
  3733. const C_DATATYPE_SPECIAL_DATE = 80;
  3734. /**
  3735. * Special type datetime for store date-time values: YYYY-MM-DD HH:II:SS
  3736. * @var integer
  3737. */
  3738. const C_DATATYPE_SPECIAL_DATETIME = 81;
  3739. /**
  3740. *
  3741. * DATA TYPE
  3742. * Specified. This means the developer or DBA
  3743. * has altered the column to a different type not
  3744. * recognized by RedBean. This high number makes sure
  3745. * it will not be converted back to another type by accident.
  3746. * @var integer
  3747. */
  3748. const C_DATATYPE_SPECIFIED = 99;
  3749. /**
  3750. * Holds the RedBean Database Adapter.
  3751. * @var RedBean_Adapter_DBAdapter
  3752. */
  3753. protected $adapter;
  3754. /**
  3755. * character to escape keyword table/column names
  3756. * @var string
  3757. */
  3758. protected $quoteCharacter = '`';
  3759. /**
  3760. * Do everything that needs to be done to format a table name.
  3761. *
  3762. * @param string $name of table
  3763. *
  3764. * @return string table name
  3765. */
  3766. public function safeTable($name, $noQuotes = false) {
  3767. $name = strtolower($name);
  3768. $name = $this->check($name);
  3769. if (!$noQuotes) $name = $this->noKW($name);
  3770. return $name;
  3771. }
  3772. /**
  3773. * Do everything that needs to be done to format a column name.
  3774. *
  3775. * @param string $name of column
  3776. *
  3777. * @return string $column name
  3778. */
  3779. public function safeColumn($name, $noQuotes = false) {
  3780. $name = strtolower($name);
  3781. $name = $this->check($name);
  3782. if (!$noQuotes) $name = $this->noKW($name);
  3783. return $name;
  3784. }
  3785. /**
  3786. * Constructor.
  3787. * The Query Writer Constructor also sets up the database.
  3788. *
  3789. * @param RedBean_Adapter_DBAdapter $adapter adapter
  3790. *
  3791. */
  3792. public function __construct( RedBean_Adapter $adapter ) {
  3793. $this->typeno_sqltype = array(
  3794. RedBean_QueryWriter_CUBRID::C_DATATYPE_INTEGER => ' INTEGER ',
  3795. RedBean_QueryWriter_CUBRID::C_DATATYPE_DOUBLE => ' DOUBLE ',
  3796. RedBean_QueryWriter_CUBRID::C_DATATYPE_STRING => ' STRING ',
  3797. RedBean_QueryWriter_CUBRID::C_DATATYPE_SPECIAL_DATE => ' DATE ',
  3798. RedBean_QueryWriter_CUBRID::C_DATATYPE_SPECIAL_DATETIME => ' DATETIME ',
  3799. );
  3800. $this->sqltype_typeno = array();
  3801. foreach($this->typeno_sqltype as $k=>$v)
  3802. $this->sqltype_typeno[trim(($v))]=$k;
  3803. $this->sqltype_typeno['STRING(1073741823)'] = static::C_DATATYPE_STRING;
  3804. $this->adapter = $adapter;
  3805. parent::__construct();
  3806. }
  3807. /**
  3808. * This method returns the datatype to be used for primary key IDS and
  3809. * foreign keys. Returns one if the data type constants.
  3810. *
  3811. * @return integer $const data type to be used for IDS.
  3812. */
  3813. public function getTypeForID() {
  3814. return static::C_DATATYPE_INTEGER;
  3815. }
  3816. /**
  3817. * Returns all tables in the database.
  3818. *
  3819. * @return array $tables tables
  3820. */
  3821. public function getTables() {
  3822. $rows = $this->adapter->getCol( "SELECT class_name FROM db_class WHERE is_system_class = 'NO';" );
  3823. return $rows;
  3824. }
  3825. /**
  3826. * Creates an empty, column-less table for a bean based on it's type.
  3827. * This function creates an empty table for a bean. It uses the
  3828. * safeTable() function to convert the type name to a table name.
  3829. *
  3830. * @param string $table type of bean you want to create a table for
  3831. *
  3832. * @return void
  3833. */
  3834. public function createTable( $table ) {
  3835. $rawTable = $this->safeTable($table,true);
  3836. $table = $this->safeTable($table);
  3837. $sql = ' CREATE TABLE '.$table.' (
  3838. "id" integer AUTO_INCREMENT,
  3839. CONSTRAINT "pk_'.$rawTable.'_id" PRIMARY KEY("id")
  3840. )';
  3841. $this->adapter->exec( $sql );
  3842. }
  3843. /**
  3844. * Returns an array containing the column names of the specified table.
  3845. *
  3846. * @param string $table table
  3847. *
  3848. * @return array $columns columns
  3849. */
  3850. public function getColumns( $table ) {
  3851. $columns = array();
  3852. $table = $this->safeTable($table);
  3853. $columnsRaw = $this->adapter->get("SHOW COLUMNS FROM $table");
  3854. foreach($columnsRaw as $r) {
  3855. $columns[$r['Field']]=$r['Type'];
  3856. }
  3857. return $columns;
  3858. }
  3859. /**
  3860. * Returns the Column Type Code (integer) that corresponds
  3861. * to the given value type.
  3862. *
  3863. * @param string $value value
  3864. *
  3865. * @return integer $type type
  3866. */
  3867. public function scanType( $value, $flagSpecial=false ) {
  3868. $this->svalue = $value;
  3869. if (is_null($value)) {
  3870. return static::C_DATATYPE_INTEGER;
  3871. }
  3872. if ($flagSpecial) {
  3873. if (preg_match('/^\d{4}\-\d\d-\d\d$/',$value)) {
  3874. return static::C_DATATYPE_SPECIAL_DATE;
  3875. }
  3876. if (preg_match('/^\d{4}\-\d\d-\d\d\s\d\d:\d\d:\d\d$/',$value)) {
  3877. return static::C_DATATYPE_SPECIAL_DATETIME;
  3878. }
  3879. }
  3880. $value = strval($value);
  3881. if (!$this->startsWithZeros($value)) {
  3882. if (is_numeric($value) && (floor($value)==$value) && $value >= -2147483647 && $value <= 2147483647 ) {
  3883. return static::C_DATATYPE_INTEGER;
  3884. }
  3885. if (is_numeric($value)) {
  3886. return static::C_DATATYPE_DOUBLE;
  3887. }
  3888. }
  3889. return static::C_DATATYPE_STRING;
  3890. }
  3891. /**
  3892. * Returns the Type Code for a Column Description.
  3893. * Given an SQL column description this method will return the corresponding
  3894. * code for the writer. If the include specials flag is set it will also
  3895. * return codes for special columns. Otherwise special columns will be identified
  3896. * as specified columns.
  3897. *
  3898. * @param string $typedescription description
  3899. * @param boolean $includeSpecials whether you want to get codes for special columns as well
  3900. *
  3901. * @return integer $typecode code
  3902. */
  3903. public function code( $typedescription, $includeSpecials = false ) {
  3904. $r = ((isset($this->sqltype_typeno[$typedescription])) ? $this->sqltype_typeno[$typedescription] : static::C_DATATYPE_SPECIFIED);
  3905. if ($includeSpecials) return $r;
  3906. if ($r > static::C_DATATYPE_SPECIFIED) return static::C_DATATYPE_SPECIFIED;
  3907. return $r;
  3908. }
  3909. /**
  3910. * This method adds a column to a table.
  3911. * This methods accepts a type and infers the corresponding table name.
  3912. *
  3913. * @param string $type name of the table
  3914. * @param string $column name of the column
  3915. * @param integer $field data type for field
  3916. *
  3917. * @return void
  3918. *
  3919. */
  3920. public function addColumn( $type, $column, $field ) {
  3921. $table = $type;
  3922. $type = $field;
  3923. $table = $this->safeTable($table);
  3924. $column = $this->safeColumn($column);
  3925. $type = array_key_exists($type, $this->typeno_sqltype) ? $this->typeno_sqltype[$type] : '';
  3926. $sql = "ALTER TABLE $table ADD COLUMN $column $type ";
  3927. $this->adapter->exec( $sql );
  3928. }
  3929. /**
  3930. * This method upgrades the column to the specified data type.
  3931. * This methods accepts a type and infers the corresponding table name.
  3932. *
  3933. * @param string $type type / table that needs to be adjusted
  3934. * @param string $column column that needs to be altered
  3935. * @param integer $datatype target data type
  3936. *
  3937. * @return void
  3938. */
  3939. public function widenColumn( $type, $column, $datatype ) {
  3940. $table = $type;
  3941. $type = $datatype;
  3942. $table = $this->safeTable($table);
  3943. $column = $this->safeColumn($column);
  3944. $newtype = array_key_exists($type, $this->typeno_sqltype) ? $this->typeno_sqltype[$type] : '';
  3945. $changecolumnSQL = "ALTER TABLE $table CHANGE $column $column $newtype ";
  3946. $this->adapter->exec( $changecolumnSQL );
  3947. }
  3948. /**
  3949. * Adds a Unique index constrain to the table.
  3950. *
  3951. * @param string $table table
  3952. * @param string $col1 column
  3953. * @param string $col2 column
  3954. *
  3955. * @return void
  3956. */
  3957. public function addUniqueIndex( $table,$columns ) {
  3958. $table = $this->safeTable($table);
  3959. sort($columns); //else we get multiple indexes due to order-effects
  3960. foreach($columns as $k=>$v) {
  3961. $columns[$k]= $this->safeColumn($v);
  3962. }
  3963. $r = $this->adapter->get("SHOW INDEX FROM $table");
  3964. $name = 'UQ_'.sha1(implode(',',$columns));
  3965. if ($r) {
  3966. foreach($r as $i) {
  3967. if (strtoupper($i['Key_name'])== strtoupper($name)) {
  3968. return;
  3969. }
  3970. }
  3971. }
  3972. $sql = "ALTER TABLE $table
  3973. ADD CONSTRAINT UNIQUE $name (".implode(',',$columns).")";
  3974. $this->adapter->exec($sql);
  3975. }
  3976. /**
  3977. * Tests whether a given SQL state is in the list of states.
  3978. *
  3979. * @param string $state code
  3980. * @param array $list array of sql states
  3981. *
  3982. * @return boolean $yesno occurs in list
  3983. */
  3984. public function sqlStateIn($state, $list) {
  3985. /*$stateMap = array(
  3986. 'HY000'=>RedBean_QueryWriter::C_SQLSTATE_NO_SUCH_TABLE,
  3987. '42S22'=>RedBean_QueryWriter::C_SQLSTATE_NO_SUCH_COLUMN,
  3988. 'HY000'=>RedBean_QueryWriter::C_SQLSTATE_INTEGRITY_CONSTRAINT_VIOLATION
  3989. );*/
  3990. if ($state=='HY000') {
  3991. if (in_array(RedBean_QueryWriter::C_SQLSTATE_INTEGRITY_CONSTRAINT_VIOLATION,$list)) return true;
  3992. if (in_array(RedBean_QueryWriter::C_SQLSTATE_NO_SUCH_COLUMN,$list)) return true;
  3993. if (in_array(RedBean_QueryWriter::C_SQLSTATE_NO_SUCH_TABLE,$list)) return true;
  3994. }
  3995. return false;
  3996. //return in_array((isset($stateMap[$state]) ? $stateMap[$state] : '0'),$list);
  3997. }
  3998. /**
  3999. * Adds a constraint. If one of the beans gets trashed
  4000. * the other, related bean should be removed as well.
  4001. *
  4002. * @param RedBean_OODBBean $bean1 first bean
  4003. * @param RedBean_OODBBean $bean2 second bean
  4004. * @param bool $dontCache by default we use a cache, TRUE = NO CACHING (optional)
  4005. *
  4006. * @return void
  4007. */
  4008. public function addConstraint( RedBean_OODBBean $bean1, RedBean_OODBBean $bean2) {
  4009. $table1 = $bean1->getMeta('type');
  4010. $table2 = $bean2->getMeta('type');
  4011. $writer = $this;
  4012. $adapter = $this->adapter;
  4013. $table = RedBean_QueryWriter_AQueryWriter::getAssocTableFormat( array( $table1,$table2) );
  4014. $property1 = $bean1->getMeta('type') . '_id';
  4015. $property2 = $bean2->getMeta('type') . '_id';
  4016. if ($property1==$property2) $property2 = $bean2->getMeta('type').'2_id';
  4017. //Dispatch to right method
  4018. return $this->constrain($table, $table1, $table2, $property1, $property2);
  4019. }
  4020. /**
  4021. * Add the constraints for a specific database driver: CUBRID
  4022. * @todo Too many arguments; find a way to solve this in a neater way.
  4023. *
  4024. * @param string $table table
  4025. * @param string $table1 table1
  4026. * @param string $table2 table2
  4027. * @param string $property1 property1
  4028. * @param string $property2 property2
  4029. *
  4030. * @return boolean $succes whether the constraint has been applied
  4031. */
  4032. protected function constrain($table, $table1, $table2, $property1, $property2) {
  4033. $writer = $this;
  4034. $adapter = $this->adapter;
  4035. $firstState = $this->buildFK($table,$table1,$property1,'id',true);
  4036. $secondState = $this->buildFK($table,$table2,$property2,'id',true);
  4037. return ($firstState && $secondState);
  4038. }
  4039. /**
  4040. * This method adds a foreign key from type and field to
  4041. * target type and target field.
  4042. * The foreign key is created without an action. On delete/update
  4043. * no action will be triggered. The FK is only used to allow database
  4044. * tools to generate pretty diagrams and to make it easy to add actions
  4045. * later on.
  4046. * This methods accepts a type and infers the corresponding table name.
  4047. *
  4048. *
  4049. * @param string $type type that will have a foreign key field
  4050. * @param string $targetType points to this type
  4051. * @param string $field field that contains the foreign key value
  4052. * @param string $targetField field where the fk points to
  4053. *
  4054. * @return void
  4055. */
  4056. public function addFK( $type, $targetType, $field, $targetField, $isDependent = false) {
  4057. return $this->buildFK($type, $targetType, $field, $targetField, $isDependent);
  4058. }
  4059. /**
  4060. * This method adds a foreign key from type and field to
  4061. * target type and target field.
  4062. * The foreign key is created without an action. On delete/update
  4063. * no action will be triggered. The FK is only used to allow database
  4064. * tools to generate pretty diagrams and to make it easy to add actions
  4065. * later on.
  4066. * This methods accepts a type and infers the corresponding table name.
  4067. *
  4068. *
  4069. * @param string $type type that will have a foreign key field
  4070. * @param string $targetType points to this type
  4071. * @param string $field field that contains the foreign key value
  4072. * @param string $targetField field where the fk points to
  4073. *
  4074. * @return void
  4075. */
  4076. protected function buildFK($type, $targetType, $field, $targetField,$isDep=false) {
  4077. $table = $this->safeTable($type);
  4078. $tableNoQ = $this->safeTable($type,true);
  4079. $targetTable = $this->safeTable($targetType);
  4080. $targetTableNoQ = $this->safeTable($targetType,true);
  4081. $column = $this->safeColumn($field);
  4082. $columnNoQ = $this->safeColumn($field,true);
  4083. $targetColumn = $this->safeColumn($targetField);
  4084. $targetColumnNoQ = $this->safeColumn($targetField,true);
  4085. $keys = $this->getKeys($targetTableNoQ,$tableNoQ);
  4086. $needsToAddFK = true;
  4087. $needsToDropFK = false;
  4088. foreach($keys as $key) {
  4089. if ($key['FKTABLE_NAME']==$tableNoQ && $key['FKCOLUMN_NAME']==$columnNoQ) {
  4090. //already has an FK
  4091. $needsToDropFK = true;
  4092. if ((($isDep && $key['DELETE_RULE']==0) || (!$isDep && $key['DELETE_RULE']==3))) {
  4093. return false;
  4094. }
  4095. }
  4096. }
  4097. if ($needsToDropFK) {
  4098. $sql = "ALTER TABLE $table DROP FOREIGN KEY {$key['FK_NAME']} ";
  4099. $this->adapter->exec($sql);
  4100. }
  4101. $casc = ($isDep ? 'CASCADE' : 'SET NULL');
  4102. $sql = "ALTER TABLE $table ADD CONSTRAINT FOREIGN KEY($column) REFERENCES $targetTable($targetColumn) ON DELETE $casc ";
  4103. $this->adapter->exec($sql);
  4104. }
  4105. /**
  4106. * Drops all tables in database
  4107. */
  4108. public function wipeAll() {
  4109. foreach($this->getTables() as $t) {
  4110. foreach($this->getKeys($t) as $k) {
  4111. $this->adapter->exec("ALTER TABLE \"{$k['FKTABLE_NAME']}\" DROP FOREIGN KEY \"{$k['FK_NAME']}\"");
  4112. }
  4113. $this->adapter->exec("DROP TABLE \"$t\"");
  4114. }
  4115. foreach($this->getTables() as $t) {
  4116. $this->adapter->exec("DROP TABLE \"$t\"");
  4117. }
  4118. }
  4119. /**
  4120. * Obtains the keys of a table using the PDO schema function.
  4121. *
  4122. * @param type $table
  4123. * @return type
  4124. */
  4125. protected function getKeys($table,$table2=null) {
  4126. $pdo = $this->adapter->getDatabase()->getPDO();
  4127. $keys = $pdo->cubrid_schema(PDO::CUBRID_SCH_EXPORTED_KEYS,$table);//print_r($keys);
  4128. if ($table2) $keys = array_merge($keys, $pdo->cubrid_schema(PDO::CUBRID_SCH_IMPORTED_KEYS,$table2) );//print_r($keys);
  4129. return $keys;
  4130. }
  4131. /**
  4132. * Checks table name or column name.
  4133. *
  4134. * @param string $table table string
  4135. *
  4136. * @return string $table escaped string
  4137. */
  4138. protected function check($table) {
  4139. if ($this->quoteCharacter && strpos($table, $this->quoteCharacter)!==false) {
  4140. throw new Redbean_Exception_Security('Illegal chars in table name');
  4141. }
  4142. return $table;
  4143. }
  4144. }
  4145. /**
  4146. * RedBean Exception Base
  4147. *
  4148. * @file RedBean/Exception.php
  4149. * @description Represents the base class
  4150. * for RedBean Exceptions
  4151. * @author Gabor de Mooij and the RedBeanPHP Community
  4152. * @license BSD/GPLv2
  4153. *
  4154. *
  4155. * copyright (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community
  4156. * This source file is subject to the BSD/GPLv2 License that is bundled
  4157. * with this source code in the file license.txt.
  4158. */
  4159. class RedBean_Exception extends Exception {}
  4160. /**
  4161. * RedBean Exception SQL
  4162. *
  4163. * @file RedBean/Exception/SQL.php
  4164. * @description Represents a generic database exception independent of the
  4165. * underlying driver.
  4166. * @author Gabor de Mooij and the RedBeanPHP Community
  4167. * @license BSD/GPLv2
  4168. *
  4169. *
  4170. * (c) copyright G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community.
  4171. * This source file is subject to the BSD/GPLv2 License that is bundled
  4172. * with this source code in the file license.txt.
  4173. */
  4174. class RedBean_Exception_SQL extends Exception {
  4175. /**
  4176. * Holds the current SQL Strate code.
  4177. * @var string
  4178. */
  4179. private $sqlState;
  4180. /**
  4181. * Returns an ANSI-92 compliant SQL state.
  4182. *
  4183. * @return string $state ANSI state code
  4184. */
  4185. public function getSQLState() {
  4186. return $this->sqlState;
  4187. }
  4188. /**
  4189. * @todo parse state to verify valid ANSI92!
  4190. * Stores ANSI-92 compliant SQL state.
  4191. *
  4192. * @param string $sqlState code
  4193. *
  4194. * @return void
  4195. */
  4196. public function setSQLState( $sqlState ) {
  4197. $this->sqlState = $sqlState;
  4198. }
  4199. /**
  4200. * To String prints both code and SQL state.
  4201. *
  4202. * @return string $message prints this exception instance as a string
  4203. */
  4204. public function __toString() {
  4205. return '['.$this->getSQLState().'] - '.$this->getMessage();
  4206. }
  4207. }
  4208. /**
  4209. * Exception Security.
  4210. * Part of the RedBean Exceptions Mechanism.
  4211. *
  4212. * @file RedBean/Exception
  4213. * @description Represents a subtype in the RedBean Exception System.
  4214. * @author Gabor de Mooij and the RedBeanPHP Community
  4215. * @license BSD/GPLv2
  4216. *
  4217. *
  4218. * (c) copyright G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community.
  4219. * This source file is subject to the BSD/GPLv2 License that is bundled
  4220. * with this source code in the file license.txt.
  4221. */
  4222. class RedBean_Exception_Security extends RedBean_Exception {}
  4223. /**
  4224. * RedBean Object Oriented DataBase
  4225. *
  4226. * @file RedBean/OODB.php
  4227. * @description RedBean OODB
  4228. *
  4229. * @author Gabor de Mooij and the RedBeanPHP community
  4230. * @license BSD/GPLv2
  4231. *
  4232. * The RedBean OODB Class is the main class of RedBeanPHP.
  4233. * It takes RedBean_OODBBean objects and stores them to and loads them from the
  4234. * database as well as providing other CRUD functions. This class acts as a
  4235. * object database.
  4236. *
  4237. * copyright (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community
  4238. * This source file is subject to the BSD/GPLv2 License that is bundled
  4239. * with this source code in the file license.txt.
  4240. */
  4241. class RedBean_OODB extends RedBean_Observable {
  4242. /**
  4243. * Chill mode, for fluid mode but with a list of beans / types that
  4244. * are considered to be stable and don't need to be modified.
  4245. * @var array
  4246. */
  4247. private $chillList = array();
  4248. /**
  4249. * List of dependencies. Format: $type => array($depensOnMe, $andMe)
  4250. * @var array
  4251. */
  4252. private $dep = array();
  4253. /**
  4254. * Secret stash. Used for batch loading.
  4255. * @var array
  4256. */
  4257. private $stash = NULL;
  4258. /**
  4259. * Contains the writer for OODB.
  4260. * @var RedBean_Adapter_DBAdapter
  4261. */
  4262. private $writer;
  4263. /**
  4264. * Whether this instance of OODB is frozen or not.
  4265. * In frozen mode the schema will not de modified, in fluid mode
  4266. * the schema can be adjusted to meet the needs of the developer.
  4267. * @var boolean
  4268. */
  4269. private $isFrozen = false;
  4270. /**
  4271. * Bean Helper. The bean helper to give to the beans. Bean Helpers
  4272. * assist beans in getting hold of a toolbox.
  4273. * @var null|\RedBean_BeanHelperFacade
  4274. */
  4275. private $beanhelper = null;
  4276. /**
  4277. * The RedBean OODB Class is the main class of RedBean.
  4278. * It takes RedBean_OODBBean objects and stores them to and loads them from the
  4279. * database as well as providing other CRUD functions. This class acts as a
  4280. * object database.
  4281. * Constructor, requires a DBAadapter (dependency inversion)
  4282. * @param RedBean_Adapter_DBAdapter $adapter
  4283. */
  4284. public function __construct( $writer ) {
  4285. if ($writer instanceof RedBean_QueryWriter) {
  4286. $this->writer = $writer;
  4287. }
  4288. $this->beanhelper = new RedBean_BeanHelperFacade();
  4289. }
  4290. /**
  4291. * Toggles fluid or frozen mode. In fluid mode the database
  4292. * structure is adjusted to accomodate your objects. In frozen mode
  4293. * this is not the case.
  4294. *
  4295. * You can also pass an array containing a selection of frozen types.
  4296. * Let's call this chilly mode, it's just like fluid mode except that
  4297. * certain types (i.e. tables) aren't touched.
  4298. *
  4299. * @param boolean|array $trueFalse
  4300. */
  4301. public function freeze( $tf ) {
  4302. if (is_array($tf)) {
  4303. $this->chillList = $tf;
  4304. $this->isFrozen = false;
  4305. }
  4306. else
  4307. $this->isFrozen = (boolean) $tf;
  4308. }
  4309. /**
  4310. * Returns the current mode of operation of RedBean.
  4311. * In fluid mode the database
  4312. * structure is adjusted to accomodate your objects.
  4313. * In frozen mode
  4314. * this is not the case.
  4315. *
  4316. * @return boolean $yesNo TRUE if frozen, FALSE otherwise
  4317. */
  4318. public function isFrozen() {
  4319. return (bool) $this->isFrozen;
  4320. }
  4321. /**
  4322. * Dispenses a new bean (a RedBean_OODBBean Bean Object)
  4323. * of the specified type. Always
  4324. * use this function to get an empty bean object. Never
  4325. * instantiate a RedBean_OODBBean yourself because it needs
  4326. * to be configured before you can use it with RedBean. This
  4327. * function applies the appropriate initialization /
  4328. * configuration for you.
  4329. *
  4330. * @param string $type type of bean you want to dispense
  4331. *
  4332. * @return RedBean_OODBBean $bean the new bean instance
  4333. */
  4334. public function dispense($type ) {
  4335. $this->signal( 'before_dispense', $type );
  4336. $bean = new RedBean_OODBBean();
  4337. $bean->setBeanHelper($this->beanhelper);
  4338. $bean->setMeta('type',$type );
  4339. $bean->setMeta('sys.id','id');
  4340. $bean->id = 0;
  4341. if (!$this->isFrozen) $this->check( $bean );
  4342. $bean->setMeta('tainted',true);
  4343. $this->signal('dispense',$bean );
  4344. return $bean;
  4345. }
  4346. /**
  4347. * Sets bean helper to be given to beans.
  4348. * Bean helpers assist beans in getting a reference to a toolbox.
  4349. *
  4350. * @param RedBean_IBeanHelper $beanhelper helper
  4351. *
  4352. * @return void
  4353. */
  4354. public function setBeanHelper( RedBean_IBeanHelper $beanhelper) {
  4355. $this->beanhelper = $beanhelper;
  4356. }
  4357. /**
  4358. * Checks whether a RedBean_OODBBean bean is valid.
  4359. * If the type is not valid or the ID is not valid it will
  4360. * throw an exception: RedBean_Exception_Security.
  4361. * @throws RedBean_Exception_Security $exception
  4362. *
  4363. * @param RedBean_OODBBean $bean the bean that needs to be checked
  4364. *
  4365. * @return void
  4366. */
  4367. public function check( RedBean_OODBBean $bean ) {
  4368. //Is all meta information present?
  4369. if (!isset($bean->id) ) {
  4370. throw new RedBean_Exception_Security('Bean has incomplete Meta Information id ');
  4371. }
  4372. if (!($bean->getMeta('type'))) {
  4373. throw new RedBean_Exception_Security('Bean has incomplete Meta Information II');
  4374. }
  4375. //Pattern of allowed characters
  4376. $pattern = '/[^a-z0-9_]/i';
  4377. //Does the type contain invalid characters?
  4378. if (preg_match($pattern,$bean->getMeta('type'))) {
  4379. throw new RedBean_Exception_Security('Bean Type is invalid');
  4380. }
  4381. //Are the properties and values valid?
  4382. foreach($bean as $prop=>$value) {
  4383. if (
  4384. is_array($value) ||
  4385. (is_object($value)) ||
  4386. strlen($prop)<1 ||
  4387. preg_match($pattern,$prop)
  4388. ) {
  4389. throw new RedBean_Exception_Security("Invalid Bean: property $prop ");
  4390. }
  4391. }
  4392. }
  4393. /**
  4394. * Searches the database for a bean that matches conditions $conditions and sql $addSQL
  4395. * and returns an array containing all the beans that have been found.
  4396. *
  4397. * Conditions need to take form:
  4398. *
  4399. * array(
  4400. * 'PROPERTY' => array( POSSIBLE VALUES... 'John','Steve' )
  4401. * 'PROPERTY' => array( POSSIBLE VALUES... )
  4402. * );
  4403. *
  4404. * All conditions are glued together using the AND-operator, while all value lists
  4405. * are glued using IN-operators thus acting as OR-conditions.
  4406. *
  4407. * Note that you can use property names; the columns will be extracted using the
  4408. * appropriate bean formatter.
  4409. *
  4410. * @throws RedBean_Exception_SQL
  4411. *
  4412. * @param string $type type of beans you are looking for
  4413. * @param array $conditions list of conditions
  4414. * @param string $addSQL SQL to be used in query
  4415. * @param boolean $all whether you prefer to use a WHERE clause or not (TRUE = not)
  4416. *
  4417. * @return array $beans resulting beans
  4418. */
  4419. public function find($type,$conditions=array(),$addSQL=null,$all=false) {
  4420. try {
  4421. $beans = $this->convertToBeans($type,$this->writer->selectRecord($type,$conditions,$addSQL,false,false,$all));
  4422. return $beans;
  4423. }catch(RedBean_Exception_SQL $e) {
  4424. if (!$this->writer->sqlStateIn($e->getSQLState(),
  4425. array(
  4426. RedBean_QueryWriter::C_SQLSTATE_NO_SUCH_TABLE,
  4427. RedBean_QueryWriter::C_SQLSTATE_NO_SUCH_COLUMN)
  4428. )) throw $e;
  4429. }
  4430. return array();
  4431. }
  4432. /**
  4433. * Checks whether the specified table already exists in the database.
  4434. * Not part of the Object Database interface!
  4435. *
  4436. * @param string $table table name (not type!)
  4437. *
  4438. * @return boolean $exists whether the given table exists within this database.
  4439. */
  4440. public function tableExists($table) {
  4441. //does this table exist?
  4442. $tables = $this->writer->getTables();
  4443. return in_array(($table), $tables);
  4444. }
  4445. /**
  4446. * Processes all column based build commands.
  4447. * A build command is an additional instruction for the Query Writer. It is processed only when
  4448. * a column gets created. The build command is often used to instruct the writer to write some
  4449. * extra SQL to create indexes or constraints. Build commands are stored in meta data of the bean.
  4450. * They are only for internal use, try to refrain from using them in your code directly.
  4451. *
  4452. * @param string $table name of the table to process build commands for
  4453. * @param string $property name of the property to process build commands for
  4454. * @param RedBean_OODBBean $bean bean that contains the build commands
  4455. *
  4456. * @return void
  4457. */
  4458. protected function processBuildCommands($table, $property, RedBean_OODBBean $bean) {
  4459. if ($inx = ($bean->getMeta('buildcommand.indexes'))) {
  4460. if (isset($inx[$property])) $this->writer->addIndex($table,$inx[$property],$property);
  4461. }
  4462. }
  4463. /**
  4464. * Process groups. Internal function. Processes different kind of groups for
  4465. * storage function. Given a list of original beans and a list of current beans,
  4466. * this function calculates which beans remain in the list (residue), which
  4467. * have been deleted (are in the trashcan) and which beans have been added
  4468. * (additions).
  4469. *
  4470. * @param array $originals originals
  4471. * @param array $current the current beans
  4472. * @param array $additions beans that have been added
  4473. * @param array $trashcan beans that have been deleted
  4474. * @param array $residue beans that have been left untouched
  4475. *
  4476. * @return array $result new relational state
  4477. */
  4478. private function processGroups( $originals, $current, $additions, $trashcan, $residue ) {
  4479. return array(
  4480. array_merge($additions,array_diff($current,$originals)),
  4481. array_merge($trashcan,array_diff($originals,$current)),
  4482. array_merge($residue,array_intersect($current,$originals))
  4483. );
  4484. }
  4485. /**
  4486. * Figures out the desired type given the cast string ID.
  4487. *
  4488. * @param string $cast cast identifier
  4489. *
  4490. * @return integer $typeno
  4491. */
  4492. private function getTypeFromCast($cast) {
  4493. if ($cast=='string') {
  4494. $typeno = $this->writer->scanType('STRING');
  4495. }
  4496. elseif ($cast=='id') {
  4497. $typeno = $this->writer->getTypeForID();
  4498. }
  4499. elseif(isset($this->writer->sqltype_typeno[$cast])) {
  4500. $typeno = $this->writer->sqltype_typeno[$cast];
  4501. }
  4502. else {
  4503. throw new RedBean_Exception('Invalid Cast');
  4504. }
  4505. return $typeno;
  4506. }
  4507. /**
  4508. * Processes an embedded bean. First the bean gets unboxed if possible.
  4509. * Then, the bean is stored if needed and finally the ID of the bean
  4510. * will be returned.
  4511. *
  4512. * @param RedBean_OODBBean|Model $v the bean or model
  4513. *
  4514. * @return integer $id
  4515. */
  4516. private function prepareEmbeddedBean($v) {
  4517. if (!$v->id || $v->getMeta('tainted')) {
  4518. $this->store($v);
  4519. }
  4520. return $v->id;
  4521. }
  4522. /**
  4523. * Stores a bean in the database. This function takes a
  4524. * RedBean_OODBBean Bean Object $bean and stores it
  4525. * in the database. If the database schema is not compatible
  4526. * with this bean and RedBean runs in fluid mode the schema
  4527. * will be altered to store the bean correctly.
  4528. * If the database schema is not compatible with this bean and
  4529. * RedBean runs in frozen mode it will throw an exception.
  4530. * This function returns the primary key ID of the inserted
  4531. * bean.
  4532. *
  4533. * @throws RedBean_Exception_Security $exception
  4534. *
  4535. * @param RedBean_OODBBean | RedBean_SimpleModel $bean bean to store
  4536. *
  4537. * @return integer $newid resulting ID of the new bean
  4538. */
  4539. public function store( $bean ) {
  4540. if ($bean instanceof RedBean_SimpleModel) $bean = $bean->unbox();
  4541. if (!($bean instanceof RedBean_OODBBean)) throw new RedBean_Exception_Security('OODB Store requires a bean, got: '.gettype($bean));
  4542. $processLists = false;
  4543. foreach($bean as $k=>$v) if (is_array($v) || is_object($v)) { $processLists = true; break; }
  4544. if (!$processLists && !$bean->getMeta('tainted')) return $bean->getID();
  4545. $this->signal('update', $bean );
  4546. foreach($bean as $k=>$v) if (is_array($v) || is_object($v)) { $processLists = true; break; }
  4547. if ($processLists) {
  4548. //Define groups
  4549. $sharedAdditions = $sharedTrashcan = $sharedresidue = $sharedItems = array();
  4550. $ownAdditions = $ownTrashcan = $ownresidue = array();
  4551. $tmpCollectionStore = array();
  4552. $embeddedBeans = array();
  4553. foreach($bean as $p=>$v) {
  4554. if ($v instanceof RedBean_SimpleModel) $v = $v->unbox();
  4555. if ($v instanceof RedBean_OODBBean) {
  4556. $linkField = $p.'_id';
  4557. $bean->$linkField = $this->prepareEmbeddedBean($v);
  4558. $bean->setMeta('cast.'.$linkField,'id');
  4559. $embeddedBeans[$linkField] = $v;
  4560. $tmpCollectionStore[$p]=$bean->$p;
  4561. $bean->removeProperty($p);
  4562. }
  4563. if (is_array($v)) {
  4564. $originals = $bean->getMeta('sys.shadow.'.$p);
  4565. if (!$originals) $originals = array();
  4566. if (strpos($p,'own')===0) {
  4567. list($ownAdditions,$ownTrashcan,$ownresidue)=$this->processGroups($originals,$v,$ownAdditions,$ownTrashcan,$ownresidue);
  4568. $bean->removeProperty($p);
  4569. }
  4570. elseif (strpos($p,'shared')===0) {
  4571. list($sharedAdditions,$sharedTrashcan,$sharedresidue)=$this->processGroups($originals,$v,$sharedAdditions,$sharedTrashcan,$sharedresidue);
  4572. $bean->removeProperty($p);
  4573. }
  4574. else {}
  4575. }
  4576. }
  4577. }
  4578. $this->storeBean($bean);
  4579. if ($processLists) {
  4580. $this->processEmbeddedBeans($bean,$embeddedBeans);
  4581. $myFieldLink = $bean->getMeta('type').'_id';
  4582. //Handle related beans
  4583. $this->processTrashcan($bean,$ownTrashcan);
  4584. $this->processAdditions($bean,$ownAdditions);
  4585. $this->processResidue($ownresidue);
  4586. foreach($sharedTrashcan as $trash) $this->assocManager->unassociate($trash,$bean);
  4587. $this->processSharedAdditions($bean,$sharedAdditions);
  4588. foreach($sharedresidue as $residue) $this->store($residue);
  4589. }
  4590. $this->signal('after_update',$bean);
  4591. return (int) $bean->id;
  4592. }
  4593. /**
  4594. * Stores a cleaned bean; i.e. only scalar values. This is the core of the store()
  4595. * method. When all lists and embedded beans (parent objects) have been processed and
  4596. * removed from the original bean the bean is passed to this method to be stored
  4597. * in the database.
  4598. *
  4599. * @param RedBean_OODBBean $bean the clean bean
  4600. */
  4601. private function storeBean(RedBean_OODBBean $bean) {
  4602. if (!$this->isFrozen) $this->check($bean);
  4603. //what table does it want
  4604. $table = $bean->getMeta('type');
  4605. if ($bean->getMeta('tainted')) {
  4606. //Does table exist? If not, create
  4607. if (!$this->isFrozen && !$this->tableExists($this->writer->safeTable($table,true))) {
  4608. $this->writer->createTable( $table );
  4609. $bean->setMeta('buildreport.flags.created',true);
  4610. }
  4611. if (!$this->isFrozen) {
  4612. $columns = $this->writer->getColumns($table) ;
  4613. }
  4614. //does the table fit?
  4615. $insertvalues = array();
  4616. $insertcolumns = array();
  4617. $updatevalues = array();
  4618. foreach( $bean as $p=>$v ) {
  4619. $origV = $v;
  4620. if ($p!='id') {
  4621. if (!$this->isFrozen) {
  4622. //Not in the chill list?
  4623. if (!in_array($bean->getMeta('type'),$this->chillList)) {
  4624. //Does the user want to specify the type?
  4625. if ($bean->getMeta("cast.$p",-1)!==-1) {
  4626. $cast = $bean->getMeta("cast.$p");
  4627. $typeno = $this->getTypeFromCast($cast);
  4628. }
  4629. else {
  4630. $cast = false;
  4631. //What kind of property are we dealing with?
  4632. $typeno = $this->writer->scanType($v,true);
  4633. $v = $this->writer->getValue();
  4634. }
  4635. //Is this property represented in the table?
  4636. if (isset($columns[$this->writer->safeColumn($p,true)])) {
  4637. //rescan
  4638. $v = $origV;
  4639. if (!$cast) $typeno = $this->writer->scanType($v,false);
  4640. //yes it is, does it still fit?
  4641. $sqlt = $this->writer->code($columns[$this->writer->safeColumn($p,true)]);
  4642. if ($typeno > $sqlt) {
  4643. //no, we have to widen the database column type
  4644. $this->writer->widenColumn( $table, $p, $typeno );
  4645. $bean->setMeta('buildreport.flags.widen',true);
  4646. }
  4647. }
  4648. else {
  4649. //no it is not
  4650. $this->writer->addColumn($table, $p, $typeno);
  4651. $bean->setMeta('buildreport.flags.addcolumn',true);
  4652. //@todo: move build commands here... more practical
  4653. $this->processBuildCommands($table,$p,$bean);
  4654. }
  4655. }
  4656. }
  4657. //Okay, now we are sure that the property value will fit
  4658. $insertvalues[] = $v;
  4659. $insertcolumns[] = $p;
  4660. $updatevalues[] = array( 'property'=>$p, 'value'=>$v );
  4661. }
  4662. }
  4663. if (!$this->isFrozen && ($uniques = $bean->getMeta('buildcommand.unique'))) {
  4664. foreach($uniques as $unique) $this->writer->addUniqueIndex( $table, $unique );
  4665. }
  4666. $rs = $this->writer->updateRecord( $table, $updatevalues, $bean->id );
  4667. $bean->id = $rs;
  4668. $bean->setMeta('tainted',false);
  4669. }
  4670. }
  4671. /**
  4672. * Processes a list of beans from a bean. A bean may contain lists. This
  4673. * method handles shared addition lists; i.e. the $bean->sharedObject properties.
  4674. *
  4675. * @param RedBean_OODBBean $bean the bean
  4676. * @param array $sharedAdditions list with shared additions
  4677. */
  4678. private function processSharedAdditions($bean,$sharedAdditions) {
  4679. foreach($sharedAdditions as $addition) {
  4680. if ($addition instanceof RedBean_OODBBean) {
  4681. $this->assocManager->associate($addition,$bean);
  4682. }
  4683. else {
  4684. throw new RedBean_Exception_Security('Array may only contain RedBean_OODBBeans');
  4685. }
  4686. }
  4687. }
  4688. /**
  4689. * Processes a list of beans from a bean. A bean may contain lists. This
  4690. * method handles own lists; i.e. the $bean->ownObject properties.
  4691. * A residue is a bean in an own-list that stays where it is. This method
  4692. * checks if there have been any modification to this bean, in that case
  4693. * the bean is stored once again, otherwise the bean will be left untouched.
  4694. *
  4695. * @param RedBean_OODBBean $bean the bean
  4696. * @param array $ownresidue list
  4697. */
  4698. private function processResidue($ownresidue) {
  4699. foreach($ownresidue as $residue) {
  4700. if ($residue->getMeta('tainted')) {
  4701. $this->store($residue);
  4702. }
  4703. }
  4704. }
  4705. /**
  4706. * Processes a list of beans from a bean. A bean may contain lists. This
  4707. * method handles own lists; i.e. the $bean->ownObject properties.
  4708. * A trash can bean is a bean in an own-list that has been removed
  4709. * (when checked with the shadow). This method
  4710. * checks if the bean is also in the dependency list. If it is the bean will be removed.
  4711. * If not, the connection between the bean and the owner bean will be broken by
  4712. * setting the ID to NULL.
  4713. *
  4714. * @param RedBean_OODBBean $bean the bean
  4715. * @param array $ownTrashcan list
  4716. */
  4717. private function processTrashcan($bean,$ownTrashcan) {
  4718. $myFieldLink = $bean->getMeta('type').'_id';
  4719. foreach($ownTrashcan as $trash) {
  4720. if (isset($this->dep[$trash->getMeta('type')]) && in_array($bean->getMeta('type'),$this->dep[$trash->getMeta('type')])) {
  4721. $this->trash($trash);
  4722. }
  4723. else {
  4724. $trash->$myFieldLink = null;
  4725. $this->store($trash);
  4726. }
  4727. }
  4728. }
  4729. /**
  4730. * Processes embedded beans.
  4731. * Each embedded bean will be indexed and foreign keys will
  4732. * be created if the bean is in the dependency list.
  4733. *
  4734. * @param RedBean_OODBBean $bean bean
  4735. * @param array $embeddedBeans embedded beans
  4736. */
  4737. private function processEmbeddedBeans($bean, $embeddedBeans) {
  4738. foreach($embeddedBeans as $linkField=>$embeddedBean) {
  4739. if (!$this->isFrozen) {
  4740. $this->writer->addIndex($bean->getMeta('type'),
  4741. 'index_foreignkey_'.$bean->getMeta('type').'_'.$embeddedBean->getMeta('type'),
  4742. $linkField);
  4743. $isDep = $this->isDependentOn($bean->getMeta('type'),$embeddedBean->getMeta('type'));
  4744. $this->writer->addFK($bean->getMeta('type'),$embeddedBean->getMeta('type'),$linkField,'id',$isDep);
  4745. }
  4746. }
  4747. }
  4748. /**
  4749. * Part of the store() functionality.
  4750. * Handles all new additions after the bean has been saved.
  4751. * Stores addition bean in own-list, extracts the id and
  4752. * adds a foreign key. Also adds a constraint in case the type is
  4753. * in the dependent list.
  4754. *
  4755. * @param RedBean_OODBBean $bean bean
  4756. * @param array $ownAdditions list of addition beans in own-list
  4757. */
  4758. private function processAdditions($bean,$ownAdditions) {
  4759. $myFieldLink = $bean->getMeta('type').'_id';
  4760. foreach($ownAdditions as $addition) {
  4761. if ($addition instanceof RedBean_OODBBean) {
  4762. $addition->$myFieldLink = $bean->id;
  4763. $addition->setMeta('cast.'.$myFieldLink,'id');
  4764. $this->store($addition);
  4765. if (!$this->isFrozen) {
  4766. $this->writer->addIndex($addition->getMeta('type'),
  4767. 'index_foreignkey_'.$addition->getMeta('type').'_'.$bean->getMeta('type'),
  4768. $myFieldLink);
  4769. $isDep = $this->isDependentOn($addition->getMeta('type'),$bean->getMeta('type'));
  4770. $this->writer->addFK($addition->getMeta('type'),$bean->getMeta('type'),$myFieldLink,'id',$isDep);
  4771. }
  4772. }
  4773. else {
  4774. throw new RedBean_Exception_Security('Array may only contain RedBean_OODBBeans');
  4775. }
  4776. }
  4777. }
  4778. /**
  4779. * Checks whether reference type has been marked as dependent on target type.
  4780. * This is the result of setting reference type as a key in R::dependencies() and
  4781. * putting target type in its array.
  4782. *
  4783. * @param string $refType reference type
  4784. * @param string $otherType other type / target type
  4785. *
  4786. * @return boolean
  4787. */
  4788. protected function isDependentOn($refType,$otherType) {
  4789. return (boolean) (isset($this->dep[$refType]) && in_array($otherType,$this->dep[$refType]));
  4790. }
  4791. /**
  4792. * Loads a bean from the object database.
  4793. * It searches for a RedBean_OODBBean Bean Object in the
  4794. * database. It does not matter how this bean has been stored.
  4795. * RedBean uses the primary key ID $id and the string $type
  4796. * to find the bean. The $type specifies what kind of bean you
  4797. * are looking for; this is the same type as used with the
  4798. * dispense() function. If RedBean finds the bean it will return
  4799. * the RedBean_OODB Bean object; if it cannot find the bean
  4800. * RedBean will return a new bean of type $type and with
  4801. * primary key ID 0. In the latter case it acts basically the
  4802. * same as dispense().
  4803. *
  4804. * Important note:
  4805. * If the bean cannot be found in the database a new bean of
  4806. * the specified type will be generated and returned.
  4807. *
  4808. * @param string $type type of bean you want to load
  4809. * @param integer $id ID of the bean you want to load
  4810. *
  4811. * @return RedBean_OODBBean $bean loaded bean
  4812. */
  4813. public function load($type,$id) {
  4814. $this->signal('before_open',array('type'=>$type,'id'=>$id));
  4815. $bean = $this->dispense( $type );
  4816. if ($this->stash && isset($this->stash[$id])) {
  4817. $row = $this->stash[$id];
  4818. }
  4819. else {
  4820. try {
  4821. $rows = $this->writer->selectRecord($type,array('id'=>array($id)));
  4822. }catch(RedBean_Exception_SQL $e ) {
  4823. if (
  4824. $this->writer->sqlStateIn($e->getSQLState(),
  4825. array(
  4826. RedBean_QueryWriter::C_SQLSTATE_NO_SUCH_COLUMN,
  4827. RedBean_QueryWriter::C_SQLSTATE_NO_SUCH_TABLE)
  4828. )
  4829. ) {
  4830. $rows = 0;
  4831. if ($this->isFrozen) throw $e; //only throw if frozen;
  4832. }
  4833. }
  4834. if (!$rows) return $bean; // $this->dispense($type); -- no need...
  4835. $row = array_pop($rows);
  4836. }
  4837. foreach($row as $p=>$v) {
  4838. //populate the bean with the database row
  4839. $bean->$p = $v;
  4840. }
  4841. $this->signal('open',$bean );
  4842. $bean->setMeta('tainted',false);
  4843. return $bean;
  4844. }
  4845. /**
  4846. * Removes a bean from the database.
  4847. * This function will remove the specified RedBean_OODBBean
  4848. * Bean Object from the database.
  4849. *
  4850. * @throws RedBean_Exception_Security $exception
  4851. *
  4852. * @param RedBean_OODBBean|RedBean_SimpleModel $bean bean you want to remove from database
  4853. */
  4854. public function trash( $bean ) {
  4855. if ($bean instanceof RedBean_SimpleModel) $bean = $bean->unbox();
  4856. if (!($bean instanceof RedBean_OODBBean)) throw new RedBean_Exception_Security('OODB Store requires a bean, got: '.gettype($bean));
  4857. $this->signal('delete',$bean);
  4858. foreach($bean as $p=>$v) {
  4859. if ($v instanceof RedBean_OODBBean) {
  4860. $bean->removeProperty($p);
  4861. }
  4862. if (is_array($v)) {
  4863. if (strpos($p,'own')===0) {
  4864. $bean->removeProperty($p);
  4865. }
  4866. elseif (strpos($p,'shared')===0) {
  4867. $bean->removeProperty($p);
  4868. }
  4869. }
  4870. }
  4871. if (!$this->isFrozen) $this->check( $bean );
  4872. try {
  4873. $this->writer->selectRecord($bean->getMeta('type'),
  4874. array('id' => array( $bean->id) ),null,true );
  4875. }catch(RedBean_Exception_SQL $e) {
  4876. if (!$this->writer->sqlStateIn($e->getSQLState(),
  4877. array(
  4878. RedBean_QueryWriter::C_SQLSTATE_NO_SUCH_COLUMN,
  4879. RedBean_QueryWriter::C_SQLSTATE_NO_SUCH_TABLE)
  4880. )) throw $e;
  4881. }
  4882. //$bean->id = 0;
  4883. $this->signal('after_delete', $bean );
  4884. }
  4885. /**
  4886. * Returns an array of beans. Pass a type and a series of ids and
  4887. * this method will bring you the correspondig beans.
  4888. *
  4889. * important note: Because this method loads beans using the load()
  4890. * function (but faster) it will return empty beans with ID 0 for
  4891. * every bean that could not be located. The resulting beans will have the
  4892. * passed IDs as their keys.
  4893. *
  4894. * @param string $type type of beans
  4895. * @param array $ids ids to load
  4896. *
  4897. * @return array $beans resulting beans (may include empty ones)
  4898. */
  4899. public function batch( $type, $ids ) {
  4900. if (!$ids) return array();
  4901. $collection = array();
  4902. try {
  4903. $rows = $this->writer->selectRecord($type,array('id'=>$ids));
  4904. }catch(RedBean_Exception_SQL $e) {
  4905. if (!$this->writer->sqlStateIn($e->getSQLState(),
  4906. array(
  4907. RedBean_QueryWriter::C_SQLSTATE_NO_SUCH_COLUMN,
  4908. RedBean_QueryWriter::C_SQLSTATE_NO_SUCH_TABLE)
  4909. )) throw $e;
  4910. $rows = false;
  4911. }
  4912. $this->stash = array();
  4913. if (!$rows) return array();
  4914. foreach($rows as $row) {
  4915. $this->stash[$row['id']] = $row;
  4916. }
  4917. foreach($ids as $id) {
  4918. $collection[ $id ] = $this->load( $type, $id );
  4919. }
  4920. $this->stash = NULL;
  4921. return $collection;
  4922. }
  4923. /**
  4924. * This is a convenience method; it converts database rows
  4925. * (arrays) into beans. Given a type and a set of rows this method
  4926. * will return an array of beans of the specified type loaded with
  4927. * the data fields provided by the result set from the database.
  4928. *
  4929. * @param string $type type of beans you would like to have
  4930. * @param array $rows rows from the database result
  4931. *
  4932. * @return array $collectionOfBeans collection of beans
  4933. */
  4934. public function convertToBeans($type, $rows) {
  4935. $collection = array();
  4936. $this->stash = array();
  4937. foreach($rows as $row) {
  4938. $id = $row['id'];
  4939. $this->stash[$id] = $row;
  4940. $collection[ $id ] = $this->load( $type, $id );
  4941. }
  4942. $this->stash = NULL;
  4943. return $collection;
  4944. }
  4945. /**
  4946. * Returns the number of beans we have in DB of a given type.
  4947. *
  4948. * @param string $type type of bean we are looking for
  4949. *
  4950. * @return integer $num number of beans found
  4951. */
  4952. public function count($type) {
  4953. try {
  4954. return (int) $this->writer->count($type);
  4955. }catch(RedBean_Exception_SQL $e) {
  4956. if (!$this->writer->sqlStateIn($e->getSQLState(),
  4957. array(RedBean_QueryWriter::C_SQLSTATE_NO_SUCH_TABLE)
  4958. )) throw $e;
  4959. }
  4960. return 0;
  4961. }
  4962. /**
  4963. * Trash all beans of a given type.
  4964. *
  4965. * @param string $type type
  4966. *
  4967. * @return boolean $yesNo whether we actually did some work or not..
  4968. */
  4969. public function wipe($type) {
  4970. try {
  4971. $this->writer->wipe($type);
  4972. return true;
  4973. }catch(RedBean_Exception_SQL $e) {
  4974. if (!$this->writer->sqlStateIn($e->getSQLState(),
  4975. array(RedBean_QueryWriter::C_SQLSTATE_NO_SUCH_TABLE)
  4976. )) throw $e;
  4977. return false;
  4978. }
  4979. }
  4980. /**
  4981. * Returns an Association Manager for use with OODB.
  4982. * A simple getter function to obtain a reference to the association manager used for
  4983. * storage and more.
  4984. *
  4985. * @throws Exception
  4986. * @return RedBean_AssociationManager $assoc Association Manager
  4987. */
  4988. public function getAssociationManager() {
  4989. if (!isset($this->assocManager)) throw new Exception('No association manager available.');
  4990. return $this->assocManager;
  4991. }
  4992. /**
  4993. * Sets the association manager instance to be used by this OODB.
  4994. * A simple setter function to set the association manager to be used for storage and
  4995. * more.
  4996. *
  4997. * @param RedBean_AssociationManager $assoc sets the association manager to be used
  4998. *
  4999. * @return void
  5000. */
  5001. public function setAssociationManager(RedBean_AssociationManager $assoc) {
  5002. $this->assocManager = $assoc;
  5003. }
  5004. public function setDepList($dep) {
  5005. $this->dep = $dep;
  5006. }
  5007. }
  5008. /**
  5009. * ToolBox
  5010. * Contains most important redbean tools
  5011. *
  5012. * @file RedBean/ToolBox.php
  5013. * @description The ToolBox acts as a resource locator for RedBean but can
  5014. * be integrated in larger resource locators (nested).
  5015. * It does not do anything more than just store the three most
  5016. * important RedBean resources (tools): the database adapter,
  5017. * the redbean core class (oodb) and the query writer.
  5018. * @author Gabor de Mooij and the RedBeanPHP community
  5019. * @license BSD/GPLv2
  5020. *
  5021. *
  5022. * copyright (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community.
  5023. * This source file is subject to the BSD/GPLv2 License that is bundled
  5024. * with this source code in the file license.txt.
  5025. */
  5026. class RedBean_ToolBox {
  5027. /**
  5028. * Reference to the RedBeanPHP OODB Object Database instance
  5029. * @var RedBean_OODB
  5030. */
  5031. protected $oodb;
  5032. /**
  5033. * Reference to the Query Writer
  5034. * @var RedBean_QueryWriter
  5035. */
  5036. protected $writer;
  5037. /**
  5038. * Reference to the database adapter
  5039. * @var RedBean_Adapter_DBAdapter
  5040. */
  5041. protected $adapter;
  5042. /**
  5043. * Constructor.
  5044. * The Constructor of the ToolBox takes three arguments: a RedBean_OODB $redbean
  5045. * object database, a RedBean_Adapter $databaseAdapter and a
  5046. * RedBean_QueryWriter $writer. It stores these objects inside and acts as
  5047. * a micro service locator. You can pass the toolbox to any object that needs
  5048. * one of the RedBean core objects to interact with.
  5049. *
  5050. * @param RedBean_OODB $oodb Object Database
  5051. * @param RedBean_Adapter_DBAdapter $adapter Adapter
  5052. * @param RedBean_QueryWriter $writer Writer
  5053. *
  5054. * return RedBean_ToolBox $toolbox Toolbox
  5055. */
  5056. public function __construct(RedBean_OODB $oodb,RedBean_Adapter $adapter,RedBean_QueryWriter $writer) {
  5057. $this->oodb = $oodb;
  5058. $this->adapter = $adapter;
  5059. $this->writer = $writer;
  5060. return $this;
  5061. }
  5062. /**
  5063. * The Toolbox acts as a kind of micro service locator, providing just the
  5064. * most important objects that make up RedBean. You can pass the toolkit to
  5065. * any object that needs one of these objects to function properly.
  5066. * Returns the QueryWriter; normally you do not use this object but other
  5067. * object might want to use the default RedBean query writer to be
  5068. * database independent.
  5069. *
  5070. * @return RedBean_QueryWriter $writer writer
  5071. */
  5072. public function getWriter() {
  5073. return $this->writer;
  5074. }
  5075. /**
  5076. * The Toolbox acts as a kind of micro service locator, providing just the
  5077. * most important objects that make up RedBean. You can pass the toolkit to
  5078. * any object that needs one of these objects to function properly.
  5079. * Retruns the RedBean OODB Core object. The RedBean OODB object is
  5080. * the ultimate core of Redbean. It provides the means to store and load
  5081. * beans. Extract this object immediately after invoking a kickstart method.
  5082. *
  5083. * @return RedBean_OODB $oodb Object Database
  5084. */
  5085. public function getRedBean() {
  5086. return $this->oodb;
  5087. }
  5088. /**
  5089. * The Toolbox acts as a kind of micro service locator, providing just the
  5090. * most important objects that make up RedBean. You can pass the toolkit to
  5091. * any object that needs one of these objects to function properly.
  5092. * Returns the adapter. The Adapter can be used to perform queries
  5093. * on the database directly.
  5094. *
  5095. * @return RedBean_Adapter_DBAdapter $adapter Adapter
  5096. */
  5097. public function getDatabaseAdapter() {
  5098. return $this->adapter;
  5099. }
  5100. }
  5101. /**
  5102. * RedBean Association
  5103. *
  5104. * @file RedBean/AssociationManager.php
  5105. * @description Manages simple bean associations.
  5106. *
  5107. * @author Gabor de Mooij and the RedBeanPHP Community
  5108. * @license BSD/GPLv2
  5109. *
  5110. * (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community.
  5111. * This source file is subject to the BSD/GPLv2 License that is bundled
  5112. * with this source code in the file license.txt.
  5113. */
  5114. class RedBean_AssociationManager extends RedBean_Observable {
  5115. /**
  5116. * Contains a reference to the Object Database OODB
  5117. * @var RedBean_OODB
  5118. */
  5119. protected $oodb;
  5120. /**
  5121. * Contains a reference to the Database Adapter
  5122. * @var RedBean_Adapter_DBAdapter
  5123. */
  5124. protected $adapter;
  5125. /**
  5126. * Contains a reference to the Query Writer
  5127. * @var RedBean_QueryWriter
  5128. */
  5129. protected $writer;
  5130. /**
  5131. * Constructor
  5132. *
  5133. * @param RedBean_ToolBox $tools toolbox
  5134. */
  5135. public function __construct( RedBean_ToolBox $tools ) {
  5136. $this->oodb = $tools->getRedBean();
  5137. $this->adapter = $tools->getDatabaseAdapter();
  5138. $this->writer = $tools->getWriter();
  5139. $this->toolbox = $tools;
  5140. }
  5141. /**
  5142. * Creates a table name based on a types array.
  5143. * Manages the get the correct name for the linking table for the
  5144. * types provided.
  5145. *
  5146. * @todo find a nice way to decouple this class from QueryWriter?
  5147. *
  5148. * @param array $types 2 types as strings
  5149. *
  5150. * @return string $table table
  5151. */
  5152. public function getTable( $types ) {
  5153. return RedBean_QueryWriter_AQueryWriter::getAssocTableFormat($types);
  5154. }
  5155. /**
  5156. * Associates two beans with eachother using a many-to-many relation.
  5157. *
  5158. * @param RedBean_OODBBean $bean1 bean1
  5159. * @param RedBean_OODBBean $bean2 bean2
  5160. * @param string $table specific the table name if needed
  5161. */
  5162. public function associate(RedBean_OODBBean $bean1, RedBean_OODBBean $bean2, $table = null) {
  5163. if($table == null)
  5164. {
  5165. $table = $this->getTable( array($bean1->getMeta('type') , $bean2->getMeta('type')) );
  5166. }
  5167. $bean = $this->oodb->dispense($table);
  5168. return $this->associateBeans( $bean1, $bean2, $bean );
  5169. }
  5170. /**
  5171. * Associates a pair of beans. This method associates two beans, no matter
  5172. * what types.Accepts a base bean that contains data for the linking record.
  5173. *
  5174. * @param RedBean_OODBBean $bean1 first bean
  5175. * @param RedBean_OODBBean $bean2 second bean
  5176. * @param RedBean_OODBBean $bean base bean
  5177. *
  5178. * @return mixed $id either the link ID or null
  5179. */
  5180. protected function associateBeans(RedBean_OODBBean $bean1, RedBean_OODBBean $bean2, RedBean_OODBBean $bean) {
  5181. $property1 = $bean1->getMeta('type') . '_id';
  5182. $property2 = $bean2->getMeta('type') . '_id';
  5183. if ($property1==$property2) $property2 = $bean2->getMeta('type').'2_id';
  5184. //add a build command for Unique Indexes
  5185. $bean->setMeta('buildcommand.unique' , array(array($property1, $property2)));
  5186. //add a build command for Single Column Index (to improve performance in case unqiue cant be used)
  5187. $indexName1 = 'index_for_'.$bean->getMeta('type').'_'.$property1;
  5188. $indexName2 = 'index_for_'.$bean->getMeta('type').'_'.$property2;
  5189. $bean->setMeta('buildcommand.indexes', array($property1=>$indexName1,$property2=>$indexName2));
  5190. $this->oodb->store($bean1);
  5191. $this->oodb->store($bean2);
  5192. $bean->setMeta("cast.$property1","id");
  5193. $bean->setMeta("cast.$property2","id");
  5194. $bean->$property1 = $bean1->id;
  5195. $bean->$property2 = $bean2->id;
  5196. try {
  5197. $id = $this->oodb->store( $bean );
  5198. //On creation, add constraints....
  5199. if (!$this->oodb->isFrozen() &&
  5200. $bean->getMeta('buildreport.flags.created')){
  5201. $bean->setMeta('buildreport.flags.created',0);
  5202. if (!$this->oodb->isFrozen())
  5203. $this->writer->addConstraint( $bean1, $bean2 );
  5204. }
  5205. return $id;
  5206. }
  5207. catch(RedBean_Exception_SQL $e) {
  5208. if (!$this->writer->sqlStateIn($e->getSQLState(),
  5209. array(
  5210. RedBean_QueryWriter::C_SQLSTATE_INTEGRITY_CONSTRAINT_VIOLATION
  5211. ))) throw $e;
  5212. }
  5213. }
  5214. /**
  5215. * Returns all ids of beans of type $type that are related to $bean. If the
  5216. * $getLinks parameter is set to boolean TRUE this method will return the ids
  5217. * of the association beans instead. You can also add additional SQL. This SQL
  5218. * will be appended to the original query string used by this method. Note that this
  5219. * method will not return beans, just keys. For a more convenient method see the R-facade
  5220. * method related(), that is in fact a wrapper for this method that offers a more
  5221. * convenient solution. If you want to make use of this method, consider the
  5222. * OODB batch() method to convert the ids to beans.
  5223. *
  5224. * Since 3.2, you can now also pass an array of beans instead just one
  5225. * bean as the first parameter.
  5226. *
  5227. * @throws RedBean_Exception_SQL
  5228. *
  5229. * @param RedBean_OODBBean|array $bean reference bean
  5230. * @param string $type target type
  5231. * @param bool $getLinks whether you are interested in the assoc records
  5232. * @param bool $sql room for additional SQL
  5233. * @param string $table specific the table name if needed
  5234. *
  5235. * @return array $ids
  5236. */
  5237. public function related( $bean, $type, $getLinks=false, $sql=false, $table = null) {
  5238. if (!is_array($bean) && !($bean instanceof RedBean_OODBBean)) throw new RedBean_Exception_Security('Expected array or RedBean_OODBBean but got:'.gettype($bean));
  5239. $ids = array();
  5240. if (is_array($bean)) {
  5241. $beans = $bean;
  5242. foreach($beans as $b) {
  5243. if (!($b instanceof RedBean_OODBBean)) throw new RedBean_Exception_Security('Expected RedBean_OODBBean in array but got:'.gettype($b));
  5244. $ids[] = $b->id;
  5245. }
  5246. $bean = reset($beans);
  5247. }
  5248. else $ids[] = $bean->id;
  5249. if($table == null)
  5250. {
  5251. $table = $this->getTable( array($bean->getMeta('type') , $type) );
  5252. }
  5253. if ($type==$bean->getMeta('type')) {
  5254. $type .= '2';
  5255. $cross = 1;
  5256. }
  5257. else $cross=0;
  5258. if (!$getLinks) $targetproperty = $type.'_id'; else $targetproperty='id';
  5259. $property = $bean->getMeta('type').'_id';
  5260. try {
  5261. $sqlFetchKeys = $this->writer->selectRecord(
  5262. $table,
  5263. array( $property => $ids ),
  5264. $sql,
  5265. false
  5266. );
  5267. $sqlResult = array();
  5268. foreach( $sqlFetchKeys as $row ) {
  5269. if (isset($row[$targetproperty])) {
  5270. $sqlResult[] = $row[$targetproperty];
  5271. }
  5272. }
  5273. if ($cross) {
  5274. $sqlFetchKeys2 = $this->writer->selectRecord(
  5275. $table,
  5276. array( $targetproperty => $ids),
  5277. $sql,
  5278. false
  5279. );
  5280. foreach( $sqlFetchKeys2 as $row ) {
  5281. if (isset($row[$property])) {
  5282. $sqlResult[] = $row[$property];
  5283. }
  5284. }
  5285. }
  5286. return $sqlResult; //or returns rows in case of $sql != empty
  5287. }catch(RedBean_Exception_SQL $e) {
  5288. if (!$this->writer->sqlStateIn($e->getSQLState(),
  5289. array(
  5290. RedBean_QueryWriter::C_SQLSTATE_NO_SUCH_COLUMN,
  5291. RedBean_QueryWriter::C_SQLSTATE_NO_SUCH_TABLE)
  5292. )) throw $e;
  5293. return array();
  5294. }
  5295. }
  5296. /**
  5297. * Breaks the association between two beans. This method unassociates two beans. If the
  5298. * method succeeds the beans will no longer form an association. In the database
  5299. * this means that the association record will be removed. This method uses the
  5300. * OODB trash() method to remove the association links, thus giving FUSE models the
  5301. * opportunity to hook-in additional business logic. If the $fast parameter is
  5302. * set to boolean TRUE this method will remove the beans without their consent,
  5303. * bypassing FUSE. This can be used to improve performance.
  5304. *
  5305. * @param RedBean_OODBBean $bean1 first bean
  5306. * @param RedBean_OODBBean $bean2 second bean
  5307. * @param boolean $fast If TRUE, removes the entries by query without FUSE
  5308. * @param string $table specific the table name if needed
  5309. */
  5310. public function unassociate(RedBean_OODBBean $bean1, RedBean_OODBBean $bean2, $fast=null, $table = null) {
  5311. $this->oodb->store($bean1);
  5312. $this->oodb->store($bean2);
  5313. if($table == null)
  5314. {
  5315. $table = $this->getTable( array($bean1->getMeta('type') , $bean2->getMeta('type')) );
  5316. }
  5317. $type = $bean1->getMeta('type');
  5318. if ($type==$bean2->getMeta('type')) {
  5319. $type .= '2';
  5320. $cross = 1;
  5321. }
  5322. else $cross = 0;
  5323. $property1 = $type.'_id';
  5324. $property2 = $bean2->getMeta('type').'_id';
  5325. $value1 = (int) $bean1->id;
  5326. $value2 = (int) $bean2->id;
  5327. try {
  5328. $rows = $this->writer->selectRecord($table,array(
  5329. $property1 => array($value1), $property2=>array($value2)),null,$fast
  5330. );
  5331. if ($cross) {
  5332. $rows2 = $this->writer->selectRecord($table,array(
  5333. $property2 => array($value1), $property1=>array($value2)),null,$fast
  5334. );
  5335. if ($fast) return;
  5336. $rows = array_merge($rows,$rows2);
  5337. }
  5338. if ($fast) return;
  5339. $beans = $this->oodb->convertToBeans($table,$rows);
  5340. foreach($beans as $link) {
  5341. $this->oodb->trash($link);
  5342. }
  5343. }catch(RedBean_Exception_SQL $e) {
  5344. if (!$this->writer->sqlStateIn($e->getSQLState(),
  5345. array(
  5346. RedBean_QueryWriter::C_SQLSTATE_NO_SUCH_COLUMN,
  5347. RedBean_QueryWriter::C_SQLSTATE_NO_SUCH_TABLE)
  5348. )) throw $e;
  5349. }
  5350. return;
  5351. }
  5352. /**
  5353. * Removes all relations for a bean. This method breaks every connection between
  5354. * a certain bean $bean and every other bean of type $type. Warning: this method
  5355. * is really fast because it uses a direct SQL query however it does not inform the
  5356. * models about this. If you want to notify FUSE models about deletion use a foreach-loop
  5357. * with unassociate() instead. (that might be slower though)
  5358. *
  5359. * @param RedBean_OODBBean $bean reference bean
  5360. * @param string $type type of beans that need to be unassociated
  5361. *
  5362. * @return void
  5363. */
  5364. public function clearRelations(RedBean_OODBBean $bean, $type) {
  5365. $this->oodb->store($bean);
  5366. $table = $this->getTable( array($bean->getMeta('type') , $type) );
  5367. if ($type==$bean->getMeta('type')) {
  5368. $property2 = $type.'2_id';
  5369. $cross = 1;
  5370. }
  5371. else $cross = 0;
  5372. $property = $bean->getMeta('type').'_id';
  5373. try {
  5374. $this->writer->selectRecord( $table, array($property=>array($bean->id)),null,true);
  5375. if ($cross) {
  5376. $this->writer->selectRecord( $table, array($property2=>array($bean->id)),null,true);
  5377. }
  5378. }catch(RedBean_Exception_SQL $e) {
  5379. if (!$this->writer->sqlStateIn($e->getSQLState(),
  5380. array(
  5381. RedBean_QueryWriter::C_SQLSTATE_NO_SUCH_COLUMN,
  5382. RedBean_QueryWriter::C_SQLSTATE_NO_SUCH_TABLE)
  5383. )) throw $e;
  5384. }
  5385. }
  5386. /**
  5387. * Given two beans this function returns TRUE if they are associated using a
  5388. * many-to-many association, FALSE otherwise.
  5389. *
  5390. * @throws RedBean_Exception_SQL
  5391. *
  5392. * @param RedBean_OODBBean $bean1 bean
  5393. * @param RedBean_OODBBean $bean2 bean
  5394. *
  5395. * @return bool $related whether they are associated N-M
  5396. */
  5397. public function areRelated(RedBean_OODBBean $bean1, RedBean_OODBBean $bean2) {
  5398. if (!$bean1->getID() || !$bean2->getID()) return false;
  5399. $table = $this->getTable( array($bean1->getMeta('type') , $bean2->getMeta('type')) );
  5400. $type = $bean1->getMeta('type');
  5401. if ($type==$bean2->getMeta('type')) {
  5402. $type .= '2';
  5403. $cross = 1;
  5404. }
  5405. else $cross = 0;
  5406. $property1 = $type.'_id';
  5407. $property2 = $bean2->getMeta('type').'_id';
  5408. $value1 = (int) $bean1->id;
  5409. $value2 = (int) $bean2->id;
  5410. try {
  5411. $rows = $this->writer->selectRecord($table,array(
  5412. $property1 => array($value1), $property2=>array($value2)),null
  5413. );
  5414. if ($cross) {
  5415. $rows2 = $this->writer->selectRecord($table,array(
  5416. $property2 => array($value1), $property1=>array($value2)),null
  5417. );
  5418. $rows = array_merge($rows,$rows2);
  5419. }
  5420. }catch(RedBean_Exception_SQL $e) {
  5421. if (!$this->writer->sqlStateIn($e->getSQLState(),
  5422. array(
  5423. RedBean_QueryWriter::C_SQLSTATE_NO_SUCH_COLUMN,
  5424. RedBean_QueryWriter::C_SQLSTATE_NO_SUCH_TABLE)
  5425. )) throw $e;
  5426. return false;
  5427. }
  5428. return (count($rows)>0);
  5429. }
  5430. }
  5431. /**
  5432. * RedBean Extended Association
  5433. *
  5434. * @file RedBean/ExtAssociationManager.php
  5435. * @description Manages complex bean associations.
  5436. * @author Gabor de Mooij and the RedBeanPHP Community
  5437. * @license BSD/GPLv2
  5438. *
  5439. * copyright (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community
  5440. * This source file is subject to the BSD/GPLv2 License that is bundled
  5441. * with this source code in the file license.txt.
  5442. */
  5443. class RedBean_ExtAssociationManager extends RedBean_AssociationManager {
  5444. /**
  5445. * Associates two beans with eachother. This method connects two beans with eachother, just
  5446. * like the other associate() method in the Association Manager. The difference is however
  5447. * that this method accepts a base bean, this bean will be used as the basis of the
  5448. * association record in the link table. You can thus add additional properties and
  5449. * even foreign keys.
  5450. *
  5451. * @param RedBean_OODBBean $bean1 bean 1
  5452. * @param RedBean_OODBBean $bean2 bean 2
  5453. * @param RedBean_OODBBean $bbean base bean for association record
  5454. *
  5455. * @return void
  5456. */
  5457. public function extAssociate(RedBean_OODBBean $bean1, RedBean_OODBBean $bean2, RedBean_OODBBean $baseBean ) {
  5458. $table = $this->getTable( array($bean1->getMeta('type') , $bean2->getMeta('type')) );
  5459. $baseBean->setMeta('type', $table );
  5460. return $this->associateBeans( $bean1, $bean2, $baseBean );
  5461. }
  5462. }
  5463. /**
  5464. * RedBean Setup
  5465. * Helper class to quickly setup RedBean for you.
  5466. *
  5467. * @file RedBean/Setup.php
  5468. * @description Helper class to quickly setup RedBean for you
  5469. *
  5470. * @author Gabor de Mooij and the RedBeanPHP community
  5471. * @license BSD/GPLv2
  5472. *
  5473. *
  5474. * copyright (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community
  5475. * This source file is subject to the BSD/GPLv2 License that is bundled
  5476. * with this source code in the file license.txt.
  5477. */
  5478. class RedBean_Setup {
  5479. /**
  5480. * This method checks the DSN string. If the DSN string contains a
  5481. * database name that is not supported by RedBean yet then it will
  5482. * throw an exception RedBean_Exception_NotImplemented. In any other
  5483. * case this method will just return boolean TRUE.
  5484. * @throws RedBean_Exception_NotImplemented
  5485. * @param string $dsn
  5486. * @return boolean $true
  5487. */
  5488. private static function checkDSN($dsn) {
  5489. $dsn = trim($dsn);
  5490. $dsn = strtolower($dsn);
  5491. if (
  5492. strpos($dsn, 'mysql:')!==0
  5493. && strpos($dsn,'sqlite:')!==0
  5494. && strpos($dsn,'pgsql:')!==0
  5495. && strpos($dsn,'cubrid:')!==0
  5496. ) {
  5497. trigger_error('Unsupported DSN');
  5498. }
  5499. else {
  5500. return true;
  5501. }
  5502. }
  5503. /**
  5504. * Generic Kickstart method.
  5505. * This is the generic kickstarter. It will prepare a database connection
  5506. * using the $dsn, the $username and the $password you provide.
  5507. * If $frozen is boolean TRUE it will start RedBean in frozen mode, meaning
  5508. * that the database cannot be altered. If RedBean is started in fluid mode
  5509. * it will adjust the schema of the database if it detects an
  5510. * incompatible bean.
  5511. * This method returns a RedBean_Toolbox $toolbox filled with a
  5512. * RedBean_Adapter, a RedBean_QueryWriter and most importantly a
  5513. * RedBean_OODB; the object database. To start storing beans in the database
  5514. * simply say: $redbean = $toolbox->getRedBean(); Now you have a reference
  5515. * to the RedBean object.
  5516. * Optionally instead of using $dsn you may use an existing PDO connection.
  5517. * Example: RedBean_Setup::kickstart($existingConnection, true);
  5518. *
  5519. * @param string|PDO $dsn Database Connection String (or PDO instance)
  5520. * @param string $username Username for database
  5521. * @param string $password Password for database
  5522. * @param boolean $frozen Start in frozen mode?
  5523. *
  5524. * @return RedBean_ToolBox $toolbox
  5525. */
  5526. public static function kickstart($dsn,$username=NULL,$password=NULL,$frozen=false ) {
  5527. if ($dsn instanceof PDO) {
  5528. $pdo = new RedBean_Driver_PDO($dsn);
  5529. $dsn = $pdo->getDatabaseType() ;
  5530. }
  5531. else {
  5532. static::checkDSN($dsn);
  5533. $pdo = new RedBean_Driver_PDO($dsn,$username,$password);
  5534. }
  5535. $adapter = new RedBean_Adapter_DBAdapter($pdo);
  5536. $writerClassName = static::resolveQueryWriterTypeByDsn($dsn);
  5537. $writer = new $writerClassName($adapter);
  5538. $redbean = new RedBean_OODB($writer);
  5539. if ($frozen) $redbean->freeze(true);
  5540. $toolbox = new RedBean_ToolBox($redbean,$adapter,$writer);
  5541. //deliver everything back in a neat toolbox
  5542. return $toolbox;
  5543. }
  5544. /**
  5545. * A relevant query writer class name is resolve from dsn
  5546. * @param $dsn
  5547. * @return string name of writer class
  5548. */
  5549. protected static function resolveQueryWriterTypeByDsn($dsn)
  5550. {
  5551. if (strpos($dsn,'pgsql')===0) {
  5552. return static::resolvePostgreSQLWriterClassName();
  5553. }
  5554. else if (strpos($dsn,'sqlite')===0) {
  5555. return static::resolveSQLiteWriterClassName();
  5556. }
  5557. else if (strpos($dsn,'cubrid')===0) {
  5558. return static::resolveCubridWriterClassName();
  5559. }
  5560. else {
  5561. return static::resolveMySQLWriterClassName();
  5562. }
  5563. }
  5564. /**
  5565. * Allows overriding postgresql writer individually
  5566. * @return string name of postgresql writer class
  5567. */
  5568. protected static function resolvePostgreSQLWriterClassName()
  5569. {
  5570. return 'RedBean_QueryWriter_PostgreSQL';
  5571. }
  5572. /**
  5573. * Allows overriding sqlite writer individually
  5574. * @return string name of sqlite writer class
  5575. */
  5576. protected static function resolveSQLiteWriterClassName()
  5577. {
  5578. return 'RedBean_QueryWriter_SQLiteT';
  5579. }
  5580. /**
  5581. * Allows overriding cubrid writer individually
  5582. * @return string name of cubrid writer class
  5583. */
  5584. protected static function resolveCubridWriterClassName()
  5585. {
  5586. return 'RedBean_QueryWriter_CUBRID';
  5587. }
  5588. /**
  5589. * Allows overriding mysql writer individually
  5590. * @return string name of mysql writer class
  5591. */
  5592. protected static function resolveMySQLWriterClassName()
  5593. {
  5594. return 'RedBean_QueryWriter_MySQL';
  5595. }
  5596. }
  5597. /**
  5598. * RedBean interface for Model Formatting - Part of FUSE
  5599. *
  5600. * @file RedBean/ModelFormatter.php
  5601. * @description RedBean IModelFormatter
  5602. *
  5603. * @author Gabor de Mooij and the RedBeanPHP Community
  5604. * @license BSD/GPLv2
  5605. *
  5606. *
  5607. * copyright (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community
  5608. * This source file is subject to the BSD/GPLv2 License that is bundled
  5609. * with this source code in the file license.txt.
  5610. */
  5611. interface RedBean_IModelFormatter {
  5612. /**
  5613. * ModelHelper will call this method of the class
  5614. * you provide to discover the model
  5615. *
  5616. * @param string $model
  5617. *
  5618. * @return string $formattedModel
  5619. */
  5620. public function formatModel( $model );
  5621. }
  5622. /**
  5623. * RedBean interface for Logging
  5624. *
  5625. * @name RedBean ILogger
  5626. * @file RedBean/ILogger.php
  5627. * @author Gabor de Mooij
  5628. * @license BSD
  5629. *
  5630. *
  5631. * copyright (c) G.J.G.T. (Gabor) de Mooij
  5632. * This source file is subject to the BSD/GPLv2 License that is bundled
  5633. * with this source code in the file license.txt.
  5634. */
  5635. interface RedBean_ILogger {
  5636. /**
  5637. * Redbean will call this method to log your data
  5638. *
  5639. * @param ...
  5640. */
  5641. public function log();
  5642. }
  5643. /**
  5644. * RedBean class for Logging
  5645. *
  5646. * @name RedBean ILogger
  5647. * @file RedBean/ILogger.php
  5648. * @author Gabor de Mooij
  5649. * @license BSD
  5650. *
  5651. *
  5652. * copyright (c) G.J.G.T. (Gabor) de Mooij
  5653. * This source file is subject to the BSD/GPLv2 License that is bundled
  5654. * with this source code in the file license.txt.
  5655. */
  5656. class RedBean_Logger implements RedBean_ILogger {
  5657. /**
  5658. * Default logger method logging to STDOUT
  5659. *
  5660. * @param ...
  5661. */
  5662. public function log() {
  5663. if (func_num_args() > 0) {
  5664. foreach (func_get_args() as $argument) {
  5665. if (is_array($argument)) echo print_r($argument,true); else echo $argument;
  5666. echo "<br>\n";
  5667. }
  5668. }
  5669. }
  5670. }
  5671. /**
  5672. * RedBean Bean Helper Interface
  5673. *
  5674. * @file RedBean/IBeanHelper.php
  5675. * @description Interface for Bean Helper.
  5676. * A little bolt that glues the whole machinery together.
  5677. *
  5678. * @author Gabor de Mooij and the RedBeanPHP Community
  5679. * @license BSD/GPLv2
  5680. *
  5681. * copyright (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community
  5682. * This source file is subject to the BSD/GPLv2 License that is bundled
  5683. * with this source code in the file license.txt.
  5684. *
  5685. */
  5686. interface RedBean_IBeanHelper {
  5687. /**
  5688. * @abstract
  5689. * @return RedBean_Toolbox $toolbox toolbox
  5690. */
  5691. public function getToolbox();
  5692. public function getModelForBean(RedBean_OODBBean $bean);
  5693. }
  5694. /**
  5695. * RedBean Bean Helper
  5696. * @file RedBean/BeanHelperFacade.php
  5697. * @description Finds the toolbox for the bean.
  5698. * @author Gabor de Mooij and the RedBeanPHP Community
  5699. * @license BSD/GPLv2
  5700. *
  5701. * (c) copyright G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community
  5702. * This source file is subject to the BSD/GPLv2 License that is bundled
  5703. * with this source code in the file license.txt.
  5704. */
  5705. class RedBean_BeanHelperFacade implements RedBean_IBeanHelper {
  5706. /**
  5707. * Returns a reference to the toolbox. This method returns a toolbox
  5708. * for beans that need to use toolbox functions. Since beans can contain
  5709. * lists they need a toolbox to lazy-load their relationships.
  5710. *
  5711. * @return RedBean_ToolBox $toolbox toolbox containing all kinds of goodies
  5712. */
  5713. public function getToolbox() {
  5714. return RedBean_Facade::$toolbox;
  5715. }
  5716. /**
  5717. * Fuse connector.
  5718. * Gets the model for a bean $bean.
  5719. * Allows you to implement your own way to find the
  5720. * right model for a bean and to do dependency injection
  5721. * etc.
  5722. *
  5723. * @param RedBean_OODBBean $bean bean
  5724. *
  5725. * @return type
  5726. */
  5727. public function getModelForBean(RedBean_OODBBean $bean) {
  5728. $modelName = RedBean_ModelHelper::getModelName( $bean->getMeta('type'), $bean );
  5729. if (!class_exists($modelName)) return null;
  5730. $obj = RedBean_ModelHelper::factory($modelName);
  5731. $obj->loadBean($bean);
  5732. return $obj;
  5733. }
  5734. }
  5735. /**
  5736. * SimpleModel
  5737. *
  5738. * @file RedBean/SimpleModel.php
  5739. * @description Part of FUSE
  5740. * @author Gabor de Mooij and the RedBeanPHP Team
  5741. * @license BSD/GPLv2
  5742. *
  5743. * copyright (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community
  5744. * This source file is subject to the BSD/GPLv2 License that is bundled
  5745. * with this source code in the file license.txt.
  5746. */
  5747. class RedBean_SimpleModel {
  5748. /**
  5749. * Contains the inner bean.
  5750. * @var RedBean_OODBBean
  5751. */
  5752. protected $bean;
  5753. /**
  5754. * Used by FUSE: the ModelHelper class to connect a bean to a model.
  5755. * This method loads a bean in the model.
  5756. *
  5757. * @param RedBean_OODBBean $bean bean
  5758. */
  5759. public function loadBean( RedBean_OODBBean $bean ) {
  5760. $this->bean = $bean;
  5761. }
  5762. /**
  5763. * Magic Getter to make the bean properties available from
  5764. * the $this-scope.
  5765. *
  5766. * @param string $prop property
  5767. *
  5768. * @return mixed $propertyValue value
  5769. */
  5770. public function __get( $prop ) {
  5771. return $this->bean->$prop;
  5772. }
  5773. /**
  5774. * Magic Setter
  5775. *
  5776. * @param string $prop property
  5777. * @param mixed $value value
  5778. */
  5779. public function __set( $prop, $value ) {
  5780. $this->bean->$prop = $value;
  5781. }
  5782. /**
  5783. * Isset implementation
  5784. *
  5785. * @param string $key key
  5786. *
  5787. * @return
  5788. */
  5789. public function __isset($key) {
  5790. return (isset($this->bean->$key));
  5791. }
  5792. /**
  5793. * Box the bean using the current model.
  5794. *
  5795. * @return RedBean_SimpleModel $box a bean in a box
  5796. */
  5797. public function box() {
  5798. return $this;
  5799. }
  5800. /**
  5801. * Unbox the bean from the model.
  5802. *
  5803. * @return RedBean_OODBBean $bean bean
  5804. */
  5805. public function unbox(){
  5806. return $this->bean;
  5807. }
  5808. }
  5809. /**
  5810. * RedBean Model Helper
  5811. *
  5812. * @file RedBean/ModelHelper.php
  5813. * @description Connects beans to models, in essence
  5814. * this is the core of so-called FUSE.
  5815. *
  5816. * @author Gabor de Mooij and the RedBeanPHP Community
  5817. * @license BSD/GPLv2
  5818. *
  5819. * copyright (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community
  5820. * This source file is subject to the BSD/GPLv2 License that is bundled
  5821. * with this source code in the file license.txt.
  5822. *
  5823. */
  5824. class RedBean_ModelHelper implements RedBean_Observer {
  5825. /**
  5826. * Holds a model formatter
  5827. * @var RedBean_IModelFormatter
  5828. */
  5829. private static $modelFormatter;
  5830. /**
  5831. * Holds a dependency injector
  5832. * @var type
  5833. */
  5834. private static $dependencyInjector;
  5835. /**
  5836. * Connects OODB to a model if a model exists for that
  5837. * type of bean. This connector is used in the facade.
  5838. *
  5839. * @param string $eventName
  5840. * @param RedBean_OODBBean $bean
  5841. */
  5842. public function onEvent( $eventName, $bean ) {
  5843. $bean->$eventName();
  5844. }
  5845. /**
  5846. * Given a model ID (model identifier) this method returns the
  5847. * full model name.
  5848. *
  5849. * @param string $model
  5850. * @param RedBean_OODBBean $bean
  5851. *
  5852. * @return string $fullname
  5853. */
  5854. public static function getModelName( $model, $bean = null ) {
  5855. if (static::$modelFormatter){
  5856. return static::$modelFormatter->formatModel($model,$bean);
  5857. }
  5858. else {
  5859. return 'Model_'.ucfirst($model);
  5860. }
  5861. }
  5862. /**
  5863. * Sets the model formatter to be used to discover a model
  5864. * for Fuse.
  5865. *
  5866. * @param string $modelFormatter
  5867. */
  5868. public static function setModelFormatter( $modelFormatter ) {
  5869. static::$modelFormatter = $modelFormatter;
  5870. }
  5871. /**
  5872. * Obtains a new instance of $modelClassName, using a dependency injection
  5873. * container if possible.
  5874. *
  5875. * @param string $modelClassName name of the model
  5876. */
  5877. public static function factory( $modelClassName ) {
  5878. if (static::$dependencyInjector) {
  5879. return static::$dependencyInjector->getInstance($modelClassName);
  5880. }
  5881. return new $modelClassName();
  5882. }
  5883. /**
  5884. * Sets the dependency injector to be used.
  5885. *
  5886. * @param RedBean_DependencyInjector $di injecto to be used
  5887. */
  5888. public static function setDependencyInjector( RedBean_DependencyInjector $di ) {
  5889. static::$dependencyInjector = $di;
  5890. }
  5891. /**
  5892. * Stops the dependency injector from resolving dependencies. Removes the
  5893. * reference to the dependency injector.
  5894. */
  5895. public static function clearDependencyInjector() {
  5896. static::$dependencyInjector = null;
  5897. }
  5898. }
  5899. /**
  5900. * RedBean SQL Helper
  5901. *
  5902. * @file RedBean/SQLHelper.php
  5903. * @description Allows you to mix PHP and SQL as if they were
  5904. * a unified language
  5905. *
  5906. * Simplest case:
  5907. *
  5908. * $r->now(); //returns SQL time
  5909. *
  5910. *
  5911. * Another Example:
  5912. *
  5913. * $f->begin()
  5914. * ->select('*')
  5915. * ->from('island')->where('id = ? ')->put(1)->get();
  5916. *
  5917. * Another example:
  5918. *
  5919. * $f->begin()->show('tables')->get('col');
  5920. *
  5921. *
  5922. * @author Gabor de Mooij and the RedBeanPHP community
  5923. * @license BSD/GPLv2
  5924. *
  5925. *
  5926. * copyright (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community.
  5927. * This source file is subject to the BSD/GPLv2 License that is bundled
  5928. * with this source code in the file license.txt.
  5929. */
  5930. class RedBean_SQLHelper {
  5931. /**
  5932. * Holds the database adapter for executing SQL queries.
  5933. * @var RedBean_Adapter
  5934. */
  5935. protected $adapter;
  5936. /**
  5937. * Holds current mode
  5938. * @var boolean
  5939. */
  5940. protected $capture = false;
  5941. /**
  5942. * Holds SQL until now
  5943. * @var string
  5944. */
  5945. protected $sql = '';
  5946. /**
  5947. * Holds list of parameters for SQL Query
  5948. * @var array
  5949. */
  5950. protected $params = array();
  5951. /**
  5952. * Constructor
  5953. *
  5954. * @param RedBean_DBAdapter $adapter database adapter for querying
  5955. */
  5956. public function __construct(RedBean_Adapter $adapter) {
  5957. $this->adapter = $adapter;
  5958. }
  5959. /**
  5960. * Magic method to construct SQL query
  5961. *
  5962. * @param string $funcName name of the next SQL statement/keyword
  5963. * @param array $args list of statements to be seperated by commas
  5964. *
  5965. * @return mixed $result either self or result depending on mode
  5966. */
  5967. public function __call($funcName,$args=array()) {
  5968. return null;
  5969. $funcName = str_replace('_',' ',$funcName);
  5970. if ($this->capture) {
  5971. $this->sql .= ' '.$funcName . ' '.implode(',', $args);
  5972. return $this;
  5973. }
  5974. else {
  5975. return $this->adapter->getCell('SELECT '.$funcName.'('.implode(',',$args).')');
  5976. }
  5977. }
  5978. /**
  5979. * Begins SQL query
  5980. *
  5981. * @return RedBean_SQLHelper $this chainable
  5982. */
  5983. public function begin() {
  5984. $this->capture = true;
  5985. return $this;
  5986. }
  5987. /**
  5988. * Adds a value to the parameter list
  5989. *
  5990. * @param mixed $param parameter to be added
  5991. *
  5992. * @return RedBean_SQLHelper $this chainable
  5993. */
  5994. public function put($param) {
  5995. $this->params[] = $param;
  5996. return $this;
  5997. }
  5998. /**
  5999. * Executes query and returns result
  6000. *
  6001. * @return mixed $result
  6002. */
  6003. public function get($what='') {
  6004. $what = 'get'.ucfirst($what);
  6005. $rs = $this->adapter->$what($this->sql,$this->params);
  6006. $this->clear();
  6007. return $rs;
  6008. }
  6009. /**
  6010. * Clears the parameter list as well as the SQL query string.
  6011. *
  6012. * @return RedBean_SQLHelper $this chainable
  6013. */
  6014. public function clear() {
  6015. $this->sql = '';
  6016. $this->params = array();
  6017. $this->capture = false; //turn off capture mode (issue #142)
  6018. return $this;
  6019. }
  6020. /**
  6021. * To explicitly add a piece of SQL.
  6022. *
  6023. * @param string $sql sql
  6024. *
  6025. * @return RedBean_SQLHelper
  6026. */
  6027. public function addSQL($sql) {
  6028. if ($this->capture) {
  6029. $this->sql .= ' '.$sql . ' ';
  6030. return $this;
  6031. }
  6032. }
  6033. /**
  6034. * Returns query parts.
  6035. *
  6036. * @return array $queryParts query parts.
  6037. */
  6038. public function getQuery() {
  6039. $list = array($this->sql,$this->params);
  6040. $this->clear();
  6041. return $list;
  6042. }
  6043. /**
  6044. * Writes a '(' to the sql query.
  6045. */
  6046. public function open() {
  6047. if ($this->capture) {
  6048. $this->sql .= ' ( ';
  6049. return $this;
  6050. }
  6051. }
  6052. /**
  6053. * Writes a ')' to the sql query.
  6054. */
  6055. public function close() {
  6056. if ($this->capture) {
  6057. $this->sql .= ' ) ';
  6058. return $this;
  6059. }
  6060. }
  6061. }
  6062. /**
  6063. * RedBean Tag Manager
  6064. *
  6065. * @file RedBean/TagManager.php
  6066. * @description RedBean Tag Manager
  6067. *
  6068. * @author Gabor de Mooij and the RedBeanPHP community
  6069. * @license BSD/GPLv2
  6070. *
  6071. * Provides methods to tag beans and perform tag-based searches in the
  6072. * bean database.
  6073. *
  6074. * copyright (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community
  6075. * This source file is subject to the BSD/GPLv2 License that is bundled
  6076. * with this source code in the file license.txt.
  6077. */
  6078. class RedBean_TagManager {
  6079. /**
  6080. * The Tag Manager requires a toolbox
  6081. * @var RedBean_Toolbox
  6082. */
  6083. protected $toolbox;
  6084. /**
  6085. * Association Manager to manage tag-bean relations
  6086. * @var RedBean_AssociationManager
  6087. */
  6088. protected $associationManager;
  6089. /**
  6090. * RedBeanPHP OODB instance
  6091. * @var RedBean_OODBBean
  6092. */
  6093. protected $redbean;
  6094. /**
  6095. * Constructor,
  6096. * creates a new instance of TagManager.
  6097. * @param RedBean_Toolbox $toolbox
  6098. */
  6099. public function __construct( RedBean_Toolbox $toolbox ) {
  6100. $this->toolbox = $toolbox;
  6101. $this->redbean = $toolbox->getRedBean();
  6102. $this->associationManager = $this->redbean->getAssociationManager();
  6103. }
  6104. /**
  6105. * Finds a tag bean by it's title.
  6106. *
  6107. * @param string $title title
  6108. *
  6109. * @return RedBean_OODBBean $bean | null
  6110. */
  6111. public function findTagByTitle($title) {
  6112. $beans = $this->redbean->find('tag',array('title'=>array($title)));
  6113. if ($beans) {
  6114. return reset($beans);
  6115. }
  6116. return null;
  6117. }
  6118. /**
  6119. * Part of RedBeanPHP Tagging API.
  6120. * Tests whether a bean has been associated with one ore more
  6121. * of the listed tags. If the third parameter is TRUE this method
  6122. * will return TRUE only if all tags that have been specified are indeed
  6123. * associated with the given bean, otherwise FALSE.
  6124. * If the third parameter is FALSE this
  6125. * method will return TRUE if one of the tags matches, FALSE if none
  6126. * match.
  6127. *
  6128. * @param RedBean_OODBBean $bean bean to check for tags
  6129. * @param array $tags list of tags
  6130. * @param boolean $all whether they must all match or just some
  6131. *
  6132. * @return boolean $didMatch whether the bean has been assoc. with the tags
  6133. */
  6134. public function hasTag($bean, $tags, $all=false) {
  6135. $foundtags = $this->tag($bean);
  6136. if (is_string($foundtags)) $foundtags = explode(",",$tags);
  6137. $same = array_intersect($tags,$foundtags);
  6138. if ($all) {
  6139. return (implode(",",$same)===implode(",",$tags));
  6140. }
  6141. return (bool) (count($same)>0);
  6142. }
  6143. /**
  6144. * Part of RedBeanPHP Tagging API.
  6145. * Removes all sepcified tags from the bean. The tags specified in
  6146. * the second parameter will no longer be associated with the bean.
  6147. *
  6148. * @param RedBean_OODBBean $bean tagged bean
  6149. * @param array $tagList list of tags (names)
  6150. *
  6151. * @return void
  6152. */
  6153. public function untag($bean,$tagList) {
  6154. if ($tagList!==false && !is_array($tagList)) $tags = explode( ",", (string)$tagList); else $tags=$tagList;
  6155. foreach($tags as $tag) {
  6156. if ($t = $this->findTagByTitle($tag)) {
  6157. $this->associationManager->unassociate( $bean, $t );
  6158. }
  6159. }
  6160. }
  6161. /**
  6162. * Part of RedBeanPHP Tagging API.
  6163. * Tags a bean or returns tags associated with a bean.
  6164. * If $tagList is null or omitted this method will return a
  6165. * comma separated list of tags associated with the bean provided.
  6166. * If $tagList is a comma separated list (string) of tags all tags will
  6167. * be associated with the bean.
  6168. * You may also pass an array instead of a string.
  6169. *
  6170. * @param RedBean_OODBBean $bean bean
  6171. * @param mixed $tagList tags
  6172. *
  6173. * @return string $commaSepListTags
  6174. */
  6175. public function tag( RedBean_OODBBean $bean, $tagList = null ) {
  6176. if (is_null($tagList)) {
  6177. $tags = array();
  6178. $keys = $this->associationManager->related($bean, 'tag');
  6179. if ($keys) {
  6180. $tags = $this->redbean->batch('tag',$keys);
  6181. }
  6182. $foundTags = array();
  6183. foreach($tags as $tag) {
  6184. $foundTags[] = $tag->title;
  6185. }
  6186. return $foundTags;
  6187. }
  6188. $this->associationManager->clearRelations( $bean, 'tag' );
  6189. $this->addTags( $bean, $tagList );
  6190. }
  6191. /**
  6192. * Part of RedBeanPHP Tagging API.
  6193. * Adds tags to a bean.
  6194. * If $tagList is a comma separated list of tags all tags will
  6195. * be associated with the bean.
  6196. * You may also pass an array instead of a string.
  6197. *
  6198. * @param RedBean_OODBBean $bean bean
  6199. * @param array $tagList list of tags to add to bean
  6200. *
  6201. * @return void
  6202. */
  6203. public function addTags( RedBean_OODBBean $bean, $tagList ) {
  6204. if ($tagList!==false && !is_array($tagList)) $tags = explode( ",", (string)$tagList); else $tags=$tagList;
  6205. if ($tagList===false) return;
  6206. foreach($tags as $tag) {
  6207. if (!$t = $this->findTagByTitle($tag)) {
  6208. $t = $this->redbean->dispense('tag');
  6209. $t->title = $tag;
  6210. $this->redbean->store($t);
  6211. }
  6212. $this->associationManager->associate( $bean, $t );
  6213. }
  6214. }
  6215. /**
  6216. * Part of RedBeanPHP Tagging API.
  6217. * Returns all beans that have been tagged with one of the tags given.
  6218. *
  6219. * @param $beanType type of bean you are looking for
  6220. * @param $tagList list of tags to match
  6221. *
  6222. * @return array
  6223. */
  6224. public function tagged( $beanType, $tagList ) {
  6225. if ($tagList!==false && !is_array($tagList)) $tags = explode( ",", (string)$tagList); else $tags=$tagList;
  6226. $collection = array();
  6227. $tags = $this->redbean->find('tag',array('title'=>$tags));
  6228. if (count($tags)>0) {
  6229. $collectionKeys = $this->associationManager->related($tags,$beanType);
  6230. if ($collectionKeys) {
  6231. $collection = $this->redbean->batch($beanType,$collectionKeys);
  6232. }
  6233. }
  6234. return $collection;
  6235. }
  6236. /**
  6237. * Part of RedBeanPHP Tagging API.
  6238. * Returns all beans that have been tagged with ALL of the tags given.
  6239. *
  6240. * @param $beanType type of bean you are looking for
  6241. * @param $tagList list of tags to match
  6242. *
  6243. * @return array
  6244. */
  6245. public function taggedAll( $beanType, $tagList ) {
  6246. if ($tagList!==false && !is_array($tagList)) $tags = explode( ",", (string)$tagList); else $tags=$tagList;
  6247. $beans = array();
  6248. foreach($tags as $tag) {
  6249. $beans = $this->tagged($beanType,$tag);
  6250. if (isset($oldBeans)) $beans = array_intersect_assoc($beans,$oldBeans);
  6251. $oldBeans = $beans;
  6252. }
  6253. return $beans;
  6254. }
  6255. }
  6256. /**
  6257. * RedBean Facade
  6258. * @file RedBean/Facade.php
  6259. * @description Convenience class for RedBeanPHP.
  6260. * This class hides the object landscape of
  6261. * RedBeanPHP behind a single letter class providing
  6262. * almost all functionality with simple static calls.
  6263. * @author Gabor de Mooij and the RedBeanPHP Community
  6264. * @license BSD/GPLv2
  6265. *
  6266. * copyright (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community
  6267. * This source file is subject to the BSD/GPLv2 License that is bundled
  6268. * with this source code in the file license.txt.
  6269. *
  6270. */
  6271. class RedBean_Facade {
  6272. /**
  6273. * Collection of toolboxes
  6274. * @var array
  6275. */
  6276. public static $toolboxes = array();
  6277. /**
  6278. *
  6279. * Constains an instance of the RedBean Toolbox
  6280. * @var RedBean_ToolBox
  6281. *
  6282. */
  6283. public static $toolbox;
  6284. /**
  6285. * Constains an instance of RedBean OODB
  6286. * @var RedBean_OODB
  6287. */
  6288. public static $redbean;
  6289. /**
  6290. * Contains an instance of the Query Writer
  6291. * @var RedBean_QueryWriter
  6292. */
  6293. public static $writer;
  6294. /**
  6295. * Contains an instance of the Database
  6296. * Adapter.
  6297. * @var RedBean_DBAdapter
  6298. */
  6299. public static $adapter;
  6300. /**
  6301. * Contains an instance of the Association Manager
  6302. * @var RedBean_AssociationManager
  6303. */
  6304. public static $associationManager;
  6305. /**
  6306. * Contains an instance of the Extended Association Manager
  6307. * @var RedBean_ExtAssociationManager
  6308. */
  6309. public static $extAssocManager;
  6310. /**
  6311. * Holds an instance of Bean Exporter
  6312. * @var RedBean_Plugin_BeanExport
  6313. */
  6314. public static $exporter;
  6315. /**
  6316. * Holds the tag manager
  6317. * @var RedBean_TagManager
  6318. */
  6319. public static $tagManager;
  6320. /**
  6321. * Holds the Key of the current database.
  6322. * @var string
  6323. */
  6324. public static $currentDB = '';
  6325. /**
  6326. * Holds reference to SQL Helper
  6327. */
  6328. public static $f;
  6329. /**
  6330. * Get version
  6331. * @return string
  6332. */
  6333. public static function getVersion() {
  6334. return '3.2';
  6335. }
  6336. /**
  6337. * Kickstarts redbean for you. This method should be called before you start using
  6338. * RedBean. The Setup() method can be called without any arguments, in this case it will
  6339. * try to create a SQLite database in /tmp called red.db (this only works on UNIX-like systems).
  6340. *
  6341. * @param string $dsn Database connection string
  6342. * @param string $username Username for database
  6343. * @param string $password Password for database
  6344. *
  6345. * @return void
  6346. */
  6347. public static function setup( $dsn=NULL, $username=NULL, $password=NULL ) {
  6348. if (function_exists('sys_get_temp_dir')) $tmp = sys_get_temp_dir(); else $tmp = 'tmp';
  6349. if (is_null($dsn)) $dsn = 'sqlite:/'.$tmp.'/red.db';
  6350. static::addDatabase('default',$dsn,$username,$password);
  6351. static::selectDatabase('default');
  6352. return static::$toolbox;
  6353. }
  6354. /**
  6355. * Adds a database to the facade, afterwards you can select the database using
  6356. * selectDatabase($key).
  6357. *
  6358. * @param string $key ID for the database
  6359. * @param string $dsn DSN for the database
  6360. * @param string $user User for connection
  6361. * @param null|string $pass Password for connection
  6362. * @param bool $frozen Whether this database is frozen or not
  6363. *
  6364. * @return void
  6365. */
  6366. public static function addDatabase( $key, $dsn, $user=null, $pass=null, $frozen=false ) {
  6367. $setupClassName = static::getRedBeanSetupClassName();
  6368. static::$toolboxes[$key] = $setupClassName::kickstart($dsn,$user,$pass,$frozen);
  6369. }
  6370. /**
  6371. * Selects a different database for the Facade to work with.
  6372. *
  6373. * @param string $key Key of the database to select
  6374. * @return int 1
  6375. */
  6376. public static function selectDatabase($key) {
  6377. if (static::$currentDB===$key) return false;
  6378. static::configureFacadeWithToolbox(static::$toolboxes[$key]);
  6379. static::$currentDB = $key;
  6380. return true;
  6381. }
  6382. /**
  6383. * Toggles DEBUG mode.
  6384. * In Debug mode all SQL that happens under the hood will
  6385. * be printed to the screen or logged by provided logger.
  6386. *
  6387. * @param boolean $tf
  6388. * @param RedBean_ILogger $logger
  6389. */
  6390. public static function debug( $tf = true, $logger = NULL ) {
  6391. if (!$logger) $logger = new RedBean_Logger;
  6392. static::$adapter->getDatabase()->setDebugMode( $tf, $logger );
  6393. }
  6394. /**
  6395. * Stores a RedBean OODB Bean and returns the ID.
  6396. *
  6397. * @param RedBean_OODBBean|RedBean_SimpleModel $bean bean
  6398. *
  6399. * @return integer $id id
  6400. */
  6401. public static function store( $bean ) {
  6402. return static::$redbean->store( $bean );
  6403. }
  6404. /**
  6405. * Toggles fluid or frozen mode. In fluid mode the database
  6406. * structure is adjusted to accomodate your objects. In frozen mode
  6407. * this is not the case.
  6408. *
  6409. * You can also pass an array containing a selection of frozen types.
  6410. * Let's call this chilly mode, it's just like fluid mode except that
  6411. * certain types (i.e. tables) aren't touched.
  6412. *
  6413. * @param boolean|array $trueFalse
  6414. */
  6415. public static function freeze( $tf = true ) {
  6416. static::$redbean->freeze( $tf );
  6417. }
  6418. /**
  6419. * Loads the bean with the given type and id and returns it.
  6420. *
  6421. * @param string $type type
  6422. * @param integer $id id of the bean you want to load
  6423. *
  6424. * @return RedBean_OODBBean $bean
  6425. */
  6426. public static function load( $type, $id ) {
  6427. return static::$redbean->load( $type, $id );
  6428. }
  6429. /**
  6430. * Deletes the specified bean.
  6431. *
  6432. * @param RedBean_OODBBean|RedBean_SimpleModel $bean bean to be deleted
  6433. *
  6434. * @return mixed
  6435. */
  6436. public static function trash( $bean ) {
  6437. return static::$redbean->trash( $bean );
  6438. }
  6439. /**
  6440. * Dispenses a new RedBean OODB Bean for use with
  6441. * the rest of the methods.
  6442. *
  6443. * @param string $type type
  6444. *
  6445. *
  6446. */
  6447. public static function dispense( $type, $num = 1 ) {
  6448. if ($num==1) {
  6449. return static::$redbean->dispense( $type );
  6450. }
  6451. else {
  6452. $beans = array();
  6453. for($v=0; $v<$num; $v++) $beans[] = static::$redbean->dispense( $type );
  6454. return $beans;
  6455. }
  6456. }
  6457. /**
  6458. * Convience method. Tries to find beans of a certain type,
  6459. * if no beans are found, it dispenses a bean of that type.
  6460. *
  6461. * @param string $type type of bean you are looking for
  6462. * @param string $sql SQL code for finding the bean
  6463. * @param array $values parameters to bind to SQL
  6464. *
  6465. * @return array $beans Contains RedBean_OODBBean instances
  6466. */
  6467. public static function findOrDispense( $type, $sql, $values ) {
  6468. $foundBeans = static::find($type,$sql,$values);
  6469. if (count($foundBeans)==0) return array(static::dispense($type)); else return $foundBeans;
  6470. }
  6471. /**
  6472. * Associates two Beans. This method will associate two beans with eachother.
  6473. * You can then get one of the beans by using the related() function and
  6474. * providing the other bean. You can also provide a base bean in the extra
  6475. * parameter. This base bean allows you to add extra information to the association
  6476. * record. Note that this is for advanced use only and the information will not
  6477. * be added to one of the beans, just to the association record.
  6478. * It's also possible to provide an array or JSON string as base bean. If you
  6479. * pass a scalar this function will interpret the base bean as having one
  6480. * property called 'extra' with the value of the scalar.
  6481. *
  6482. * @param RedBean_OODBBean $bean1 bean that will be part of the association
  6483. * @param RedBean_OODBBean $bean2 bean that will be part of the association
  6484. * @param mixed $extra bean, scalar, array or JSON providing extra data.
  6485. * @param string $table specific the table name if needed
  6486. *
  6487. * @return mixed
  6488. */
  6489. public static function associate( RedBean_OODBBean $bean1, RedBean_OODBBean $bean2, $extra = null, $table = null) {
  6490. //No extra? Just associate like always (default)
  6491. if (!$extra) {
  6492. return static::$associationManager->associate( $bean1, $bean2, $table);
  6493. }
  6494. elseif($table != null)
  6495. {
  6496. throw new NotSupportedException();
  6497. }
  6498. else{
  6499. if (!is_array($extra)) {
  6500. $info = json_decode($extra,true);
  6501. if (!$info) $info = array('extra'=>$extra);
  6502. }
  6503. else {
  6504. $info = $extra;
  6505. }
  6506. $bean = RedBean_Facade::dispense('typeLess');
  6507. $bean->import($info);
  6508. return static::$extAssocManager->extAssociate($bean1, $bean2, $bean);
  6509. }
  6510. }
  6511. /**
  6512. * Breaks the association between two beans.
  6513. * This functions breaks the association between a pair of beans. After
  6514. * calling this functions the beans will no longer be associated with
  6515. * eachother. Calling related() with either one of the beans will no longer
  6516. * return the other bean.
  6517. *
  6518. * @param RedBean_OODBBean $bean1 bean
  6519. * @param RedBean_OODBBean $bean2 bean
  6520. * @param string $table specific the table name if needed
  6521. *
  6522. * @return mixed
  6523. */
  6524. public static function unassociate( RedBean_OODBBean $bean1, RedBean_OODBBean $bean2 , $fast=false, $table = null) {
  6525. return static::$associationManager->unassociate( $bean1, $bean2, $fast, $table );
  6526. }
  6527. /**
  6528. * Returns all the beans associated with $bean.
  6529. * This method will return an array containing all the beans that have
  6530. * been associated once with the associate() function and are still
  6531. * associated with the bean specified. The type parameter indicates the
  6532. * type of beans you are looking for. You can also pass some extra SQL and
  6533. * values for that SQL to filter your results after fetching the
  6534. * related beans.
  6535. *
  6536. * Dont try to make use of subqueries, a subquery using IN() seems to
  6537. * be slower than two queries!
  6538. *
  6539. * Since 3.2, you can now also pass an array of beans instead just one
  6540. * bean as the first parameter.
  6541. *
  6542. * @param RedBean_OODBBean|array $bean the bean you have
  6543. * @param string $type the type of beans you want
  6544. * @param string $sql SQL snippet for extra filtering
  6545. * @param array $val values to be inserted in SQL slots
  6546. * @param string $table specific the table name if needed
  6547. *
  6548. * @return array $beans beans yielded by your query.
  6549. */
  6550. public static function related( $bean, $type, $sql=null, $values=array(), $table = null) {
  6551. $keys = static::$associationManager->related( $bean, $type, false, false, $table );
  6552. if (count($keys)==0) return array();
  6553. if (!$sql) return static::batch($type, $keys);
  6554. $rows = static::$writer->selectRecord( $type, array('id'=>$keys),array($sql,$values),false );
  6555. return static::$redbean->convertToBeans($type,$rows);
  6556. }
  6557. /**
  6558. * Returns only single associated bean.
  6559. *
  6560. * @param RedBean_OODBBean $bean bean provided
  6561. * @param string $type type of bean you are searching for
  6562. * @param string $sql SQL for extra filtering
  6563. * @param array $values values to be inserted in SQL slots
  6564. *
  6565. *
  6566. * @return RedBean_OODBBean $bean
  6567. */
  6568. public static function relatedOne( RedBean_OODBBean $bean, $type, $sql=null, $values=array() ) {
  6569. $beans = static::related($bean, $type, $sql, $values);
  6570. if (count($beans)==0) return null;
  6571. return reset( $beans );
  6572. }
  6573. /**
  6574. * Checks whether a pair of beans is related N-M. This function does not
  6575. * check whether the beans are related in N:1 way.
  6576. *
  6577. * @param RedBean_OODBBean $bean1 first bean
  6578. * @param RedBean_OODBBean $bean2 second bean
  6579. *
  6580. * @return bool $yesNo whether they are related
  6581. */
  6582. public static function areRelated( RedBean_OODBBean $bean1, RedBean_OODBBean $bean2) {
  6583. return static::$associationManager->areRelated($bean1,$bean2);
  6584. }
  6585. /**
  6586. * The opposite of related(). Returns all the beans that are not
  6587. * associated with the bean provided.
  6588. *
  6589. * @param RedBean_OODBBean $bean bean provided
  6590. * @param string $type type of bean you are searching for
  6591. * @param string $sql SQL for extra filtering
  6592. * @param array $values values to be inserted in SQL slots
  6593. *
  6594. * @return array $beans beans
  6595. */
  6596. public static function unrelated(RedBean_OODBBean $bean, $type, $sql=null, $values=array()) {
  6597. $keys = static::$associationManager->related( $bean, $type );
  6598. $rows = static::$writer->selectRecord( $type, array('id'=>$keys), array($sql,$values), false, true );
  6599. return static::$redbean->convertToBeans($type,$rows);
  6600. }
  6601. /**
  6602. * Clears all associated beans.
  6603. * Breaks all many-to-many associations of a bean and a specified type.
  6604. *
  6605. * @param RedBean_OODBBean $bean bean you wish to clear many-to-many relations for
  6606. * @param string $type type of bean you wish to break associatons with
  6607. *
  6608. * @return void
  6609. */
  6610. public static function clearRelations( RedBean_OODBBean $bean, $type ) {
  6611. static::$associationManager->clearRelations( $bean, $type );
  6612. }
  6613. /**
  6614. * Finds a bean using a type and a where clause (SQL).
  6615. * As with most Query tools in RedBean you can provide values to
  6616. * be inserted in the SQL statement by populating the value
  6617. * array parameter; you can either use the question mark notation
  6618. * or the slot-notation (:keyname).
  6619. *
  6620. * @param string $type type the type of bean you are looking for
  6621. * @param string $sql sql SQL query to find the desired bean, starting right after WHERE clause
  6622. * @param array $values values array of values to be bound to parameters in query
  6623. *
  6624. * @return array $beans beans
  6625. */
  6626. public static function find( $type, $sql=null, $values=array() ) {
  6627. if ($sql instanceof RedBean_SQLHelper) list($sql,$values) = $sql->getQuery();
  6628. if (!is_array($values)) throw new InvalidArgumentException('Expected array, ' . gettype($values) . ' given.');
  6629. return static::$redbean->find($type,array(),array($sql,$values));
  6630. }
  6631. /**
  6632. * Finds a bean using a type and a where clause (SQL).
  6633. * As with most Query tools in RedBean you can provide values to
  6634. * be inserted in the SQL statement by populating the value
  6635. * array parameter; you can either use the question mark notation
  6636. * or the slot-notation (:keyname).
  6637. * The findAll() method differs from the find() method in that it does
  6638. * not assume a WHERE-clause, so this is valid:
  6639. *
  6640. * R::findAll('person',' ORDER BY name DESC ');
  6641. *
  6642. * Your SQL does not have to start with a valid WHERE-clause condition.
  6643. *
  6644. * @param string $type type the type of bean you are looking for
  6645. * @param string $sql sql SQL query to find the desired bean, starting right after WHERE clause
  6646. * @param array $values values array of values to be bound to parameters in query
  6647. *
  6648. * @return array $beans beans
  6649. */
  6650. public static function findAll( $type, $sql=null, $values=array() ) {
  6651. if (!is_array($values)) throw new InvalidArgumentException('Expected array, ' . gettype($values) . ' given.');
  6652. return static::$redbean->find($type,array(),array($sql,$values),true);
  6653. }
  6654. /**
  6655. * Finds a bean using a type and a where clause (SQL).
  6656. * As with most Query tools in RedBean you can provide values to
  6657. * be inserted in the SQL statement by populating the value
  6658. * array parameter; you can either use the question mark notation
  6659. * or the slot-notation (:keyname).
  6660. * The variation also exports the beans (i.e. it returns arrays).
  6661. *
  6662. * @param string $type type the type of bean you are looking for
  6663. * @param string $sql sql SQL query to find the desired bean, starting right after WHERE clause
  6664. * @param array $values values array of values to be bound to parameters in query
  6665. *
  6666. * @return array $arrays arrays
  6667. */
  6668. public static function findAndExport($type, $sql=null, $values=array()) {
  6669. $items = static::find( $type, $sql, $values );
  6670. $arr = array();
  6671. foreach($items as $key=>$item) {
  6672. $arr[$key]=$item->export();
  6673. }
  6674. return $arr;
  6675. }
  6676. /**
  6677. * Finds a bean using a type and a where clause (SQL).
  6678. * As with most Query tools in RedBean you can provide values to
  6679. * be inserted in the SQL statement by populating the value
  6680. * array parameter; you can either use the question mark notation
  6681. * or the slot-notation (:keyname).
  6682. * This variation returns the first bean only.
  6683. *
  6684. * @param string $type type the type of bean you are looking for
  6685. * @param string $sql sql SQL query to find the desired bean, starting right after WHERE clause
  6686. * @param array $values values array of values to be bound to parameters in query
  6687. *
  6688. * @return RedBean_OODBBean $bean
  6689. */
  6690. public static function findOne( $type, $sql=null, $values=array()) {
  6691. $items = static::find($type,$sql,$values);
  6692. $found = reset($items);
  6693. // if (!$found) return null;
  6694. return $found;
  6695. }
  6696. /**
  6697. * Finds a bean using a type and a where clause (SQL).
  6698. * As with most Query tools in RedBean you can provide values to
  6699. * be inserted in the SQL statement by populating the value
  6700. * array parameter; you can either use the question mark notation
  6701. * or the slot-notation (:keyname).
  6702. * This variation returns the last bean only.
  6703. *
  6704. * @param string $type type the type of bean you are looking for
  6705. * @param string $sql sql SQL query to find the desired bean, starting right after WHERE clause
  6706. * @param array $values values array of values to be bound to parameters in query
  6707. *
  6708. * @return RedBean_OODBBean $bean
  6709. */
  6710. public static function findLast( $type, $sql=null, $values=array() ) {
  6711. $items = static::find( $type, $sql, $values );
  6712. $found = end( $items );
  6713. if (!$found) return null;
  6714. return $found;
  6715. }
  6716. /**
  6717. * Returns an array of beans. Pass a type and a series of ids and
  6718. * this method will bring you the correspondig beans.
  6719. *
  6720. * important note: Because this method loads beans using the load()
  6721. * function (but faster) it will return empty beans with ID 0 for
  6722. * every bean that could not be located. The resulting beans will have the
  6723. * passed IDs as their keys.
  6724. *
  6725. * @param string $type type of beans
  6726. * @param array $ids ids to load
  6727. *
  6728. * @return array $beans resulting beans (may include empty ones)
  6729. */
  6730. public static function batch( $type, $ids ) {
  6731. return static::$redbean->batch($type, $ids);
  6732. }
  6733. /**
  6734. * Convenience function to execute Queries directly.
  6735. * Executes SQL.
  6736. *
  6737. * @param string $sql sql SQL query to execute
  6738. * @param array $values values a list of values to be bound to query parameters
  6739. *
  6740. * @return integer $affected number of affected rows
  6741. */
  6742. public static function exec( $sql, $values=array() ) {
  6743. return static::query('exec',$sql,$values);
  6744. }
  6745. /**
  6746. * Convenience function to execute Queries directly.
  6747. * Executes SQL.
  6748. *
  6749. * @param string $sql sql SQL query to execute
  6750. * @param array $values values a list of values to be bound to query parameters
  6751. *
  6752. * @return array $results
  6753. */
  6754. public static function getAll( $sql, $values=array() ) {
  6755. return static::query('get',$sql,$values);
  6756. }
  6757. /**
  6758. * Convenience function to execute Queries directly.
  6759. * Executes SQL.
  6760. *
  6761. * @param string $sql sql SQL query to execute
  6762. * @param array $values values a list of values to be bound to query parameters
  6763. *
  6764. * @return string $result scalar
  6765. */
  6766. public static function getCell( $sql, $values=array() ) {
  6767. return static::query('getCell',$sql,$values);
  6768. }
  6769. /**
  6770. * Convenience function to execute Queries directly.
  6771. * Executes SQL.
  6772. *
  6773. * @param string $sql sql SQL query to execute
  6774. * @param array $values values a list of values to be bound to query parameters
  6775. *
  6776. * @return array $results
  6777. */
  6778. public static function getRow( $sql, $values=array() ) {
  6779. return static::query('getRow',$sql,$values);
  6780. }
  6781. /**
  6782. * Convenience function to execute Queries directly.
  6783. * Executes SQL.
  6784. *
  6785. * @param string $sql sql SQL query to execute
  6786. * @param array $values values a list of values to be bound to query parameters
  6787. *
  6788. * @return array $results
  6789. */
  6790. public static function getCol( $sql, $values=array() ) {
  6791. return static::query('getCol',$sql,$values);
  6792. }
  6793. /**
  6794. * Allows overriding setup class to something else.
  6795. * @return string Name of the class we should use to setup toolbox. This should extend RedBean_Setup
  6796. */
  6797. protected static function getRedBeanSetupClassName()
  6798. {
  6799. return 'RedBean_Setup';
  6800. }
  6801. /**
  6802. * Internal Query function, executes the desired query. Used by
  6803. * all facade query functions. This keeps things DRY.
  6804. *
  6805. * @throws RedBean_Exception_SQL
  6806. *
  6807. * @param string $method desired query method (i.e. 'cell','col','exec' etc..)
  6808. * @param string $sql the sql you want to execute
  6809. * @param array $values array of values to be bound to query statement
  6810. *
  6811. * @return array $results results of query
  6812. */
  6813. private static function query($method,$sql,$values) {
  6814. if (!static::$redbean->isFrozen()) {
  6815. try {
  6816. $rs = RedBean_Facade::$adapter->$method( $sql, $values );
  6817. }catch(RedBean_Exception_SQL $e) {
  6818. if(static::$writer->sqlStateIn($e->getSQLState(),
  6819. array(
  6820. RedBean_QueryWriter::C_SQLSTATE_NO_SUCH_COLUMN,
  6821. RedBean_QueryWriter::C_SQLSTATE_NO_SUCH_TABLE)
  6822. )) {
  6823. return array();
  6824. }
  6825. else {
  6826. throw $e;
  6827. }
  6828. }
  6829. return $rs;
  6830. }
  6831. else {
  6832. return RedBean_Facade::$adapter->$method( $sql, $values );
  6833. }
  6834. }
  6835. /**
  6836. * Convenience function to execute Queries directly.
  6837. * Executes SQL.
  6838. * Results will be returned as an associative array. The first
  6839. * column in the select clause will be used for the keys in this array and
  6840. * the second column will be used for the values. If only one column is
  6841. * selected in the query, both key and value of the array will have the
  6842. * value of this field for each row.
  6843. *
  6844. * @param string $sql sql SQL query to execute
  6845. * @param array $values values a list of values to be bound to query parameters
  6846. *
  6847. * @return array $results
  6848. */
  6849. public static function getAssoc($sql,$values=array()) {
  6850. return static::query('getAssoc',$sql,$values);
  6851. }
  6852. /**
  6853. * Makes a copy of a bean. This method makes a deep copy
  6854. * of the bean.The copy will have the following features.
  6855. * - All beans in own-lists will be duplicated as well
  6856. * - All references to shared beans will be copied but not the shared beans themselves
  6857. * - All references to parent objects (_id fields) will be copied but not the parents themselves
  6858. * In most cases this is the desired scenario for copying beans.
  6859. * This function uses a trail-array to prevent infinite recursion, if a recursive bean is found
  6860. * (i.e. one that already has been processed) the ID of the bean will be returned.
  6861. * This should not happen though.
  6862. *
  6863. * Note:
  6864. * This function does a reflectional database query so it may be slow.
  6865. *
  6866. * @param RedBean_OODBBean $bean bean to be copied
  6867. * @param array $trail for internal usage, pass array()
  6868. * @param boolean $pid for internal usage
  6869. *
  6870. * @return array $copiedBean the duplicated bean
  6871. */
  6872. public static function dup($bean,$trail=array(),$pid=false) {
  6873. $duplicationManager = new RedBean_DuplicationManager(static::$toolbox);
  6874. return $duplicationManager->dup($bean, $trail,$pid);
  6875. }
  6876. /**
  6877. * Exports a collection of beans. Handy for XML/JSON exports with a
  6878. * Javascript framework like Dojo or ExtJS.
  6879. * What will be exported:
  6880. * - contents of the bean
  6881. * - all own bean lists (recursively)
  6882. * - all shared beans (not THEIR own lists)
  6883. *
  6884. * @param array|RedBean_OODBBean $beans beans to be exported
  6885. *
  6886. * @return array $array exported structure
  6887. */
  6888. public static function exportAll($beans) {
  6889. $array = array();
  6890. if (!is_array($beans)) $beans = array($beans);
  6891. foreach($beans as $bean) {
  6892. $f = static::dup($bean,array(),true);
  6893. $array[] = $f->export();
  6894. }
  6895. return $array;
  6896. }
  6897. /**
  6898. * Given an array of two beans and a property, this method
  6899. * swaps the value of the property.
  6900. * This is handy if you need to swap the priority or orderNo
  6901. * of an item (i.e. bug-tracking, page order).
  6902. *
  6903. * @param array $beans beans
  6904. * @param string $property property
  6905. */
  6906. public static function swap( $beans, $property ) {
  6907. $bean1 = array_shift($beans);
  6908. $bean2 = array_shift($beans);
  6909. $tmp = $bean1->$property;
  6910. $bean1->$property = $bean2->$property;
  6911. $bean2->$property = $tmp;
  6912. RedBean_Facade::store($bean1);
  6913. RedBean_Facade::store($bean2);
  6914. }
  6915. /**
  6916. * Converts a series of rows to beans.
  6917. *
  6918. * @param string $type type
  6919. * @param array $rows must contain an array of arrays.
  6920. *
  6921. * @return array $beans
  6922. */
  6923. public static function convertToBeans($type,$rows) {
  6924. return static::$redbean->convertToBeans($type,$rows);
  6925. }
  6926. /**
  6927. * Part of RedBeanPHP Tagging API.
  6928. * Tests whether a bean has been associated with one ore more
  6929. * of the listed tags. If the third parameter is TRUE this method
  6930. * will return TRUE only if all tags that have been specified are indeed
  6931. * associated with the given bean, otherwise FALSE.
  6932. * If the third parameter is FALSE this
  6933. * method will return TRUE if one of the tags matches, FALSE if none
  6934. * match.
  6935. *
  6936. * @param RedBean_OODBBean $bean bean to check for tags
  6937. * @param array $tags list of tags
  6938. * @param boolean $all whether they must all match or just some
  6939. *
  6940. * @return boolean $didMatch whether the bean has been assoc. with the tags
  6941. */
  6942. public static function hasTag($bean, $tags, $all=false) {
  6943. return static::$tagManager->hasTag($bean,$tags,$all);
  6944. }
  6945. /**
  6946. * Part of RedBeanPHP Tagging API.
  6947. * Removes all sepcified tags from the bean. The tags specified in
  6948. * the second parameter will no longer be associated with the bean.
  6949. *
  6950. * @param RedBean_OODBBean $bean tagged bean
  6951. * @param array $tagList list of tags (names)
  6952. *
  6953. * @return void
  6954. */
  6955. public static function untag($bean,$tagList) {
  6956. return static::$tagManager->untag($bean,$tagList);
  6957. }
  6958. /**
  6959. * Part of RedBeanPHP Tagging API.
  6960. * Tags a bean or returns tags associated with a bean.
  6961. * If $tagList is null or omitted this method will return a
  6962. * comma separated list of tags associated with the bean provided.
  6963. * If $tagList is a comma separated list (string) of tags all tags will
  6964. * be associated with the bean.
  6965. * You may also pass an array instead of a string.
  6966. *
  6967. * @param RedBean_OODBBean $bean bean
  6968. * @param mixed $tagList tags
  6969. *
  6970. * @return string $commaSepListTags
  6971. */
  6972. public static function tag( RedBean_OODBBean $bean, $tagList = null ) {
  6973. return static::$tagManager->tag($bean,$tagList);
  6974. }
  6975. /**
  6976. * Part of RedBeanPHP Tagging API.
  6977. * Adds tags to a bean.
  6978. * If $tagList is a comma separated list of tags all tags will
  6979. * be associated with the bean.
  6980. * You may also pass an array instead of a string.
  6981. *
  6982. * @param RedBean_OODBBean $bean bean
  6983. * @param array $tagList list of tags to add to bean
  6984. *
  6985. * @return void
  6986. */
  6987. public static function addTags( RedBean_OODBBean $bean, $tagList ) {
  6988. return static::$tagManager->addTags($bean,$tagList);
  6989. }
  6990. /**
  6991. * Part of RedBeanPHP Tagging API.
  6992. * Returns all beans that have been tagged with one of the tags given.
  6993. *
  6994. * @param $beanType type of bean you are looking for
  6995. * @param $tagList list of tags to match
  6996. *
  6997. * @return array
  6998. */
  6999. public static function tagged( $beanType, $tagList ) {
  7000. return static::$tagManager->tagged($beanType,$tagList);
  7001. }
  7002. /**
  7003. * Part of RedBeanPHP Tagging API.
  7004. * Returns all beans that have been tagged with ALL of the tags given.
  7005. *
  7006. * @param $beanType type of bean you are looking for
  7007. * @param $tagList list of tags to match
  7008. *
  7009. * @return array
  7010. */
  7011. public static function taggedAll( $beanType, $tagList ) {
  7012. return static::$tagManager->taggedAll($beanType,$tagList);
  7013. }
  7014. /**
  7015. * Wipes all beans of type $beanType.
  7016. *
  7017. * @param string $beanType type of bean you want to destroy entirely.
  7018. */
  7019. public static function wipe( $beanType ) {
  7020. return RedBean_Facade::$redbean->wipe($beanType);
  7021. }
  7022. /**
  7023. * Counts beans
  7024. *
  7025. * @param string $beanType type of bean
  7026. *
  7027. * @return integer $numOfBeans
  7028. */
  7029. public static function count( $beanType ) {
  7030. return RedBean_Facade::$redbean->count($beanType);
  7031. }
  7032. /**
  7033. * Configures the facade, want to have a new Writer? A new Object Database or a new
  7034. * Adapter and you want it on-the-fly? Use this method to hot-swap your facade with a new
  7035. * toolbox.
  7036. *
  7037. * @param RedBean_ToolBox $tb toolbox
  7038. *
  7039. * @return RedBean_ToolBox $tb old, rusty, previously used toolbox
  7040. */
  7041. public static function configureFacadeWithToolbox( RedBean_ToolBox $tb ) {
  7042. $oldTools = static::$toolbox;
  7043. static::$toolbox = $tb;
  7044. static::$writer = static::$toolbox->getWriter();
  7045. static::$adapter = static::$toolbox->getDatabaseAdapter();
  7046. static::$redbean = static::$toolbox->getRedBean();
  7047. static::$associationManager = new RedBean_AssociationManager( static::$toolbox );
  7048. static::$redbean->setAssociationManager(static::$associationManager);
  7049. static::$extAssocManager = new RedBean_ExtAssociationManager( static::$toolbox );
  7050. $helper = new RedBean_ModelHelper();
  7051. static::$redbean->addEventListener('update', $helper );
  7052. static::$redbean->addEventListener('open', $helper );
  7053. static::$redbean->addEventListener('delete', $helper );
  7054. static::$associationManager->addEventListener('delete', $helper );
  7055. static::$redbean->addEventListener('after_delete', $helper );
  7056. static::$redbean->addEventListener('after_update', $helper );
  7057. static::$redbean->addEventListener('dispense', $helper );
  7058. static::$tagManager = new RedBean_TagManager( static::$toolbox );
  7059. static::$f = new RedBean_SQLHelper(static::$adapter);
  7060. return $oldTools;
  7061. }
  7062. /**
  7063. * facade method for Cooker Graph.
  7064. *
  7065. * @param array $array array containing POST/GET fields or other data
  7066. * @param boolean $filterEmptyBeans whether you want to exclude empty beans
  7067. *
  7068. * @return array $arrayOfBeans Beans
  7069. */
  7070. public static function graph($array,$filterEmpty=false) {
  7071. $cooker = new RedBean_Cooker();
  7072. $cooker->setToolbox(static::$toolbox);
  7073. return $cooker->graph($array,$filterEmpty);
  7074. }
  7075. /**
  7076. * Facade Convience method for adapter transaction system.
  7077. * Begins a transaction.
  7078. *
  7079. * @return void
  7080. */
  7081. public static function begin() {
  7082. static::$adapter->startTransaction();
  7083. }
  7084. /**
  7085. * Facade Convience method for adapter transaction system.
  7086. * Commits a transaction.
  7087. *
  7088. * @return void
  7089. */
  7090. public static function commit() {
  7091. static::$adapter->commit();
  7092. }
  7093. /**
  7094. * Facade Convience method for adapter transaction system.
  7095. * Rolls back a transaction.
  7096. *
  7097. * @return void
  7098. */
  7099. public static function rollback() {
  7100. static::$adapter->rollback();
  7101. }
  7102. /**
  7103. * Returns a list of columns. Format of this array:
  7104. * array( fieldname => type )
  7105. * Note that this method only works in fluid mode because it might be
  7106. * quite heavy on production servers!
  7107. *
  7108. * @param string $table name of the table (not type) you want to get columns of
  7109. *
  7110. * @return array $columns list of columns and their types
  7111. */
  7112. public static function getColumns($table) {
  7113. return static::$writer->getColumns($table);
  7114. }
  7115. /**
  7116. * Generates question mark slots for an array of values.
  7117. *
  7118. * @param array $array
  7119. * @return string $slots
  7120. */
  7121. public static function genSlots($array) {
  7122. if (count($array)>0) {
  7123. $filler = array_fill(0,count($array),'?');
  7124. return implode(',',$filler);
  7125. }
  7126. else {
  7127. return '';
  7128. }
  7129. }
  7130. /**
  7131. * Nukes the entire database.
  7132. */
  7133. public static function nuke() {
  7134. if (!static::$redbean->isFrozen()) {
  7135. static::$writer->wipeAll();
  7136. }
  7137. }
  7138. /**
  7139. * Sets a list of dependencies.
  7140. * A dependency list contains an entry for each dependent bean.
  7141. * A dependent bean will be removed if the relation with one of the
  7142. * dependencies gets broken.
  7143. *
  7144. * Example:
  7145. *
  7146. * array(
  7147. * 'page' => array('book','magazine')
  7148. * )
  7149. *
  7150. * A page will be removed if:
  7151. *
  7152. * unset($book->ownPage[$pageID]);
  7153. *
  7154. * or:
  7155. *
  7156. * unset($magazine->ownPage[$pageID]);
  7157. *
  7158. * but not if:
  7159. *
  7160. * unset($paper->ownPage[$pageID]);
  7161. *
  7162. *
  7163. * @param array $dep list of dependencies
  7164. */
  7165. public static function dependencies($dep) {
  7166. static::$redbean->setDepList($dep);
  7167. }
  7168. /**
  7169. * Short hand function to store a set of beans at once, IDs will be
  7170. * returned as an array. For information please consult the R::store()
  7171. * function.
  7172. * A loop saver.
  7173. *
  7174. * @param array $beans list of beans to be stored
  7175. *
  7176. * @return array $ids list of resulting IDs
  7177. */
  7178. public static function storeAll($beans) {
  7179. $ids = array();
  7180. foreach($beans as $bean) $ids[] = static::store($bean);
  7181. return $ids;
  7182. }
  7183. /**
  7184. * Short hand function to trash a set of beans at once.
  7185. * For information please consult the R::trash() function.
  7186. * A loop saver.
  7187. *
  7188. * @param array $beans list of beans to be trashed
  7189. */
  7190. public static function trashAll($beans) {
  7191. foreach($beans as $bean) static::trash($bean);
  7192. }
  7193. /**
  7194. * A label is a bean with only an id, type and name property.
  7195. * This function will dispense beans for all entries in the array. The
  7196. * values of the array will be assigned to the name property of each
  7197. * individual bean.
  7198. *
  7199. * @param string $type type of beans you would like to have
  7200. * @param array $labels list of labels, names for each bean
  7201. *
  7202. * @return array $bean a list of beans with type and name property
  7203. */
  7204. public static function dispenseLabels($type,$labels) {
  7205. $labelBeans = array();
  7206. foreach($labels as $label) {
  7207. $labelBean = static::dispense($type);
  7208. $labelBean->name = $label;
  7209. $labelBeans[] = $labelBean;
  7210. }
  7211. return $labelBeans;
  7212. }
  7213. /**
  7214. * Gathers labels from beans. This function loops through the beans,
  7215. * collects the values of the name properties of each individual bean
  7216. * and stores the names in a new array. The array then gets sorted using the
  7217. * default sort function of PHP (sort).
  7218. *
  7219. * @param array $beans list of beans to loop
  7220. *
  7221. * @return array $array list of names of beans
  7222. */
  7223. public function gatherLabels($beans) {
  7224. $labels = array();
  7225. foreach($beans as $bean) $labels[] = $bean->name;
  7226. sort($labels);
  7227. return $labels;
  7228. }
  7229. /**
  7230. * Closes the database connection.
  7231. */
  7232. public static function close() {
  7233. if (isset(static::$adapter)){
  7234. static::$adapter->close();
  7235. }
  7236. }
  7237. /**
  7238. * Activates TimeLine Schema Alteration monitoring and
  7239. * Query logging.
  7240. *
  7241. * @param type $filename
  7242. */
  7243. public static function log($filename) {
  7244. $tl = new RedBean_Plugin_TimeLine($filename);
  7245. static::$adapter->addEventListener('sql_exec',$tl);
  7246. }
  7247. /**
  7248. * Simple convenience function, returns ISO date formatted representation
  7249. * of $time.
  7250. *
  7251. * @param mixed $time UNIX timestamp
  7252. *
  7253. * @return type
  7254. */
  7255. public static function isoDate( $time = null ) {
  7256. if (!$time) $time = time();
  7257. return @date('Y-m-d',$time);
  7258. }
  7259. /**
  7260. * Simple convenience function, returns ISO date time
  7261. * formatted representation
  7262. * of $time.
  7263. *
  7264. * @param mixed $time UNIX timestamp
  7265. *
  7266. * @return type
  7267. */
  7268. public static function isoDateTime( $time = null) {
  7269. if (!$time) $time = time();
  7270. return @date('Y-m-d H:i:s',$time);
  7271. }
  7272. }
  7273. //Compatibility with PHP 5.2 and earlier
  7274. function __lcfirst( $str ){ return (string)(strtolower(substr($str,0,1)).substr($str,1)); }
  7275. /**
  7276. * BeanCan
  7277. *
  7278. * @file RedBean/BeanCan.php
  7279. * @description A Server Interface for RedBean and Fuse.
  7280. * @author Gabor de Mooij and the RedBeanPHP Community
  7281. * @license BSD/GPLv2
  7282. *
  7283. * The BeanCan Server is a lightweight, minimalistic server interface for
  7284. * RedBean that can perfectly act as an ORM middleware solution or a backend
  7285. * for an AJAX application.
  7286. *
  7287. * (c) copyright G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community.
  7288. * This source file is subject to the BSD/GPLv2 License that is bundled
  7289. * with this source code in the file license.txt.
  7290. */
  7291. class RedBean_BeanCan {
  7292. /**
  7293. * Holds a FUSE instance.
  7294. * @var RedBean_ModelHelper
  7295. */
  7296. private $modelHelper;
  7297. /**
  7298. * Constructor.
  7299. */
  7300. public function __construct() {
  7301. $this->modelHelper = new RedBean_ModelHelper;
  7302. }
  7303. /**
  7304. * Writes a response object for the client (JSON encoded). Internal method.
  7305. *
  7306. * @param mixed $result result
  7307. * @param integer $id request ID
  7308. * @param integer $errorCode error code from server
  7309. * @param string $errorMessage error message from server
  7310. *
  7311. * @return string $json JSON encoded response.
  7312. */
  7313. private function resp($result=null, $id=null, $errorCode='-32603',$errorMessage='Internal Error') {
  7314. $response = array('jsonrpc'=>'2.0');
  7315. if ($id) { $response['id'] = $id; }
  7316. if ($result) {
  7317. $response['result']=$result;
  7318. }
  7319. else {
  7320. $response['error'] = array('code'=>$errorCode,'message'=>$errorMessage);
  7321. }
  7322. return (json_encode($response));
  7323. }
  7324. /**
  7325. * Processes a JSON object request.
  7326. *
  7327. * @param array $jsonObject JSON request object
  7328. *
  7329. * @return mixed $result result
  7330. */
  7331. public function handleJSONRequest( $jsonString ) {
  7332. //Decode JSON string
  7333. $jsonArray = json_decode($jsonString,true);
  7334. if (!$jsonArray) return $this->resp(null,null,-32700,'Cannot Parse JSON');
  7335. if (!isset($jsonArray['jsonrpc'])) return $this->resp(null,null,-32600,'No RPC version');
  7336. if (($jsonArray['jsonrpc']!='2.0')) return $this->resp(null,null,-32600,'Incompatible RPC Version');
  7337. //DO we have an ID to identify this request?
  7338. if (!isset($jsonArray['id'])) return $this->resp(null,null,-32600,'No ID');
  7339. //Fetch the request Identification String.
  7340. $id = $jsonArray['id'];
  7341. //Do we have a method?
  7342. if (!isset($jsonArray['method'])) return $this->resp(null,$id,-32600,'No method');
  7343. //Do we have params?
  7344. if (!isset($jsonArray['params'])) {
  7345. $data = array();
  7346. }
  7347. else {
  7348. $data = $jsonArray['params'];
  7349. }
  7350. //Check method signature
  7351. $method = explode(':',trim($jsonArray['method']));
  7352. if (count($method)!=2) {
  7353. return $this->resp(null, $id, -32600,'Invalid method signature. Use: BEAN:ACTION');
  7354. }
  7355. //Collect Bean and Action
  7356. $beanType = $method[0];
  7357. $action = $method[1];
  7358. //May not contain anything other than ALPHA NUMERIC chars and _
  7359. if (preg_match('/\W/',$beanType)) return $this->resp(null, $id, -32600,'Invalid Bean Type String');
  7360. if (preg_match('/\W/',$action)) return $this->resp(null, $id, -32600,'Invalid Action String');
  7361. try {
  7362. switch($action) {
  7363. case 'store':
  7364. if (!isset($data[0])) return $this->resp(null, $id, -32602,'First param needs to be Bean Object');
  7365. $data = $data[0];
  7366. if (!isset($data['id'])) $bean = RedBean_Facade::dispense($beanType); else
  7367. $bean = RedBean_Facade::load($beanType,$data['id']);
  7368. $bean->import( $data );
  7369. $rid = RedBean_Facade::store($bean);
  7370. return $this->resp($rid, $id);
  7371. case 'load':
  7372. if (!isset($data[0])) return $this->resp(null, $id, -32602,'First param needs to be Bean ID');
  7373. $bean = RedBean_Facade::load($beanType,$data[0]);
  7374. return $this->resp($bean->export(),$id);
  7375. case 'trash':
  7376. if (!isset($data[0])) return $this->resp(null, $id, -32602,'First param needs to be Bean ID');
  7377. $bean = RedBean_Facade::load($beanType,$data[0]);
  7378. RedBean_Facade::trash($bean);
  7379. return $this->resp('OK',$id);
  7380. default:
  7381. $modelName = $this->modelHelper->getModelName( $beanType );
  7382. if (!class_exists($modelName)) return $this->resp(null, $id, -32601,'No such bean in the can!');
  7383. $beanModel = new $modelName;
  7384. if (!method_exists($beanModel,$action)) return $this->resp(null, $id, -32601,"Method not found in Bean: $beanType ");
  7385. return $this->resp( call_user_func_array(array($beanModel,$action), $data), $id);
  7386. }
  7387. }
  7388. catch(Exception $exception) {
  7389. return $this->resp(null, $id, -32099,$exception->getCode().'-'.$exception->getMessage());
  7390. }
  7391. }
  7392. /**
  7393. * Support for RESTFul GET-requests.
  7394. * Only supports very BASIC REST requests, for more functionality please use
  7395. * the JSON-RPC 2 interface.
  7396. *
  7397. * @param string $pathToResource RESTFul path to resource
  7398. *
  7399. * @return string $json a JSON encoded response ready for sending to client
  7400. */
  7401. public function handleRESTGetRequest( $pathToResource ) {
  7402. if (!is_string($pathToResource)) return $this->resp(null,0,-32099,'IR');
  7403. $resourceInfo = explode('/',$pathToResource);
  7404. $type = $resourceInfo[0];
  7405. try {
  7406. if (count($resourceInfo) < 2) {
  7407. return $this->resp(RedBean_Facade::findAndExport($type));
  7408. }
  7409. else {
  7410. $id = (int) $resourceInfo[1];
  7411. return $this->resp(RedBean_Facade::load($type,$id)->export(),$id);
  7412. }
  7413. }
  7414. catch(Exception $e) {
  7415. return $this->resp(null,0,-32099);
  7416. }
  7417. }
  7418. }
  7419. /**
  7420. * RedBean Cooker
  7421. * @file RedBean/Cooker.php
  7422. * @description Turns arrays into bean collections for easy persistence.
  7423. * @author Gabor de Mooij and the RedBeanPHP Community
  7424. * @license BSD/GPLv2
  7425. *
  7426. * The Cooker is a little candy to make it easier to read-in an HTML form.
  7427. * This class turns a form into a collection of beans plus an array
  7428. * describing the desired associations.
  7429. *
  7430. * (c) copyright G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community.
  7431. * This source file is subject to the BSD/GPLv2 License that is bundled
  7432. * with this source code in the file license.txt.
  7433. */
  7434. class RedBean_Cooker {
  7435. /**
  7436. * This flag indicates whether empty strings in beans will be
  7437. * interpreted as NULL or not. TRUE means Yes, will be converted to NULL,
  7438. * FALSE means empty strings will be stored as such (conversion to 0 for integer fields).
  7439. * @var boolean
  7440. */
  7441. private static $useNULLForEmptyString = false;
  7442. /**
  7443. * Sets the toolbox to be used by graph()
  7444. *
  7445. * @param RedBean_Toolbox $toolbox toolbox
  7446. * @return void
  7447. */
  7448. public function setToolbox(RedBean_Toolbox $toolbox) {
  7449. $this->toolbox = $toolbox;
  7450. $this->redbean = $this->toolbox->getRedbean();
  7451. }
  7452. /**
  7453. * Turns an array (post/request array) into a collection of beans.
  7454. * Handy for turning forms into bean structures that can be stored with a
  7455. * single call.
  7456. *
  7457. * Typical usage:
  7458. *
  7459. * $struct = R::graph($_POST);
  7460. * R::store($struct);
  7461. *
  7462. * Example of a valid array:
  7463. *
  7464. * $form = array(
  7465. * 'type'=>'order',
  7466. * 'ownProduct'=>array(
  7467. * array('id'=>171,'type'=>'product'),
  7468. * ),
  7469. * 'ownCustomer'=>array(
  7470. * array('type'=>'customer','name'=>'Bill')
  7471. * ),
  7472. * 'sharedCoupon'=>array(
  7473. * array('type'=>'coupon','name'=>'123'),
  7474. * array('type'=>'coupon','id'=>3)
  7475. * )
  7476. * );
  7477. *
  7478. * Each entry in the array will become a property of the bean.
  7479. * The array needs to have a type-field indicating the type of bean it is
  7480. * going to be. The array can have nested arrays. A nested array has to be
  7481. * named conform the bean-relation conventions, i.e. ownPage/sharedPage
  7482. * each entry in the nested array represents another bean.
  7483. *
  7484. * @param array $array array to be turned into a bean collection
  7485. * @param boolean $filterEmpty whether you want to exclude empty beans
  7486. *
  7487. * @return array $beans beans
  7488. */
  7489. public function graph( $array, $filterEmpty = false ) {
  7490. $beans = array();
  7491. if (is_array($array) && isset($array['type'])) {
  7492. $type = $array['type'];
  7493. unset($array['type']);
  7494. //Do we need to load the bean?
  7495. if (isset($array['id'])) {
  7496. $id = (int) $array['id'];
  7497. $bean = $this->redbean->load($type,$id);
  7498. }
  7499. else {
  7500. $bean = $this->redbean->dispense($type);
  7501. }
  7502. foreach($array as $property=>$value) {
  7503. if (is_array($value)) {
  7504. $bean->$property = $this->graph($value,$filterEmpty);
  7505. }
  7506. else {
  7507. if($value == '' && static::$useNULLForEmptyString){
  7508. $bean->$property = null;
  7509. }
  7510. else
  7511. $bean->$property = $value;
  7512. }
  7513. }
  7514. return $bean;
  7515. }
  7516. elseif (is_array($array)) {
  7517. foreach($array as $key=>$value) {
  7518. $listBean = $this->graph($value,$filterEmpty);
  7519. if (!($listBean instanceof RedBean_OODBBean)) {
  7520. throw new RedBean_Exception_Security('Expected bean but got :'.gettype($listBean));
  7521. }
  7522. if ($listBean->isEmpty()) {
  7523. if (!$filterEmpty) {
  7524. $beans[$key] = $listBean;
  7525. }
  7526. }
  7527. else {
  7528. $beans[$key] = $listBean;
  7529. }
  7530. }
  7531. return $beans;
  7532. }
  7533. else {
  7534. throw new RedBean_Exception_Security('Expected array but got :'.gettype($array));
  7535. }
  7536. }
  7537. /**
  7538. * Toggles the use-NULL flag.
  7539. *
  7540. * @param boolean $yesNo
  7541. */
  7542. public function setUseNullFlag($yesNo) {
  7543. static::$useNULLForEmptyString = (boolean) $yesNo;
  7544. }
  7545. }
  7546. /**
  7547. * Query Logger
  7548. *
  7549. * @file RedBean/Plugin/QueryLogger.php
  7550. * @description Query logger, can be attached to an observer that
  7551. * signals the sql_exec event.
  7552. * @author Gabor de Mooij and the RedBeanPHP Community
  7553. * @license BSD/GPLv2
  7554. *
  7555. *
  7556. * (c) copyright G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community.
  7557. * This source file is subject to the BSD/GPLv2 License that is bundled
  7558. * with this source code in the file license.txt.
  7559. */
  7560. class RedBean_Plugin_QueryLogger implements RedBean_Observer {
  7561. /**
  7562. * @var array
  7563. * contains log messages
  7564. */
  7565. protected $logs = array();
  7566. /**
  7567. * Creates a new instance of the Query Logger and attaches
  7568. * this logger to the adapter.
  7569. *
  7570. * @static
  7571. * @param RedBean_Observable $adapter the adapter you want to attach to
  7572. *
  7573. * @return RedBean_Plugin_QueryLogger $querylogger instance of the Query Logger
  7574. */
  7575. public static function getInstanceAndAttach( RedBean_Observable $adapter ) {
  7576. $queryLog = new RedBean_Plugin_QueryLogger;
  7577. $adapter->addEventListener( 'sql_exec', $queryLog );
  7578. return $queryLog;
  7579. }
  7580. /**
  7581. * Singleton pattern
  7582. * Constructor - private
  7583. */
  7584. private function __construct(){}
  7585. /**
  7586. * Implementation of the onEvent() method for Observer interface.
  7587. * If a query gets executed this method gets invoked because the
  7588. * adapter will send a signal to the attached logger.
  7589. *
  7590. * @param string $eventName ID of the event (name)
  7591. * @param RedBean_DBAdapter $adapter adapter that sends the signal
  7592. *
  7593. * @return void
  7594. */
  7595. public function onEvent( $eventName, $adapter ) {
  7596. if ($eventName=='sql_exec') {
  7597. $this->logs[] = $adapter->getSQL();
  7598. }
  7599. }
  7600. /**
  7601. * Searches the logs for the given word and returns the entries found in
  7602. * the log container.
  7603. *
  7604. * @param string $word word to look for
  7605. *
  7606. * @return array $entries entries that contain the keyword
  7607. */
  7608. public function grep( $word ) {
  7609. $found = array();
  7610. foreach($this->logs as $log) {
  7611. if (strpos($log,$word)!==false) {
  7612. $found[] = $log;
  7613. }
  7614. }
  7615. return $found;
  7616. }
  7617. /**
  7618. * Returns all the logs.
  7619. *
  7620. * @return array $logs logs
  7621. */
  7622. public function getLogs() {
  7623. return $this->logs;
  7624. }
  7625. /**
  7626. * Clears the logs.
  7627. *
  7628. * @return void
  7629. */
  7630. public function clear() {
  7631. $this->logs = array();
  7632. }
  7633. }
  7634. /**
  7635. * TimeLine
  7636. *
  7637. * @file RedBean/Plugin/TimeLine.php
  7638. * @description Monitors schema changes to ease deployment.
  7639. * @author Gabor de Mooij and the RedBeanPHP Community
  7640. * @license BSD/GPLv2
  7641. *
  7642. * (c) copyright G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community.
  7643. * This source file is subject to the BSD/GPLv2 License that is bundled
  7644. * with this source code in the file license.txt.
  7645. */
  7646. class RedBean_Plugin_TimeLine extends RedBean_Plugin_QueryLogger {
  7647. /**
  7648. * Path to file to write SQL and comments to.
  7649. *
  7650. * @var string
  7651. */
  7652. protected $file;
  7653. /**
  7654. * Constructor.
  7655. * Requires a path to an existing and writable file.
  7656. *
  7657. * @param string $outputPath path to file to write schema changes to.
  7658. */
  7659. public function __construct($outputPath) {
  7660. if (!file_exists($outputPath) || !is_writable($outputPath))
  7661. throw new RedBean_Exception_Security('Cannot write to file: '.$outputPath);
  7662. $this->file = $outputPath;
  7663. }
  7664. /**
  7665. * Implementation of the onEvent() method for Observer interface.
  7666. * If a query gets executed this method gets invoked because the
  7667. * adapter will send a signal to the attached logger.
  7668. *
  7669. * @param string $eventName ID of the event (name)
  7670. * @param RedBean_DBAdapter $adapter adapter that sends the signal
  7671. *
  7672. * @return void
  7673. */
  7674. public function onEvent( $eventName, $adapter ) {
  7675. if ($eventName=='sql_exec') {
  7676. $sql = $adapter->getSQL();
  7677. $this->logs[] = $sql;
  7678. if (strpos($sql,'ALTER')===0) {
  7679. $write = "-- ".date('Y-m-d H:i')." | Altering table. \n";
  7680. $write .= $sql;
  7681. $write .= "\n\n";
  7682. }
  7683. if (strpos($sql,'CREATE')===0) {
  7684. $write = "-- ".date('Y-m-d H:i')." | Creating new table. \n";
  7685. $write .= $sql;
  7686. $write .= "\n\n";
  7687. }
  7688. if (isset($write)) {
  7689. file_put_contents($this->file,$write,FILE_APPEND);
  7690. }
  7691. }
  7692. }
  7693. }
  7694. /**
  7695. * RedBean Dependency Injector
  7696. *
  7697. * @file RedBean/DependencyInjector.php
  7698. * @description A default dependency injector that can be subclassed to
  7699. * suit your needs.
  7700. *
  7701. * @author Gabor de Mooij and the RedBeanPHP Community
  7702. * @license BSD/GPLv2
  7703. *
  7704. * copyright (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community
  7705. * This source file is subject to the BSD/GPLv2 License that is bundled
  7706. * with this source code in the file license.txt.
  7707. *
  7708. */
  7709. class RedBean_DependencyInjector {
  7710. /**
  7711. * List of dependencies.
  7712. * @var array
  7713. */
  7714. protected $dependencies = array();
  7715. /**
  7716. * Adds a dependency to the list.
  7717. * You can add dependencies using this method. Pass both the key of the
  7718. * dependency and the dependency itself. The key of the dependency is a
  7719. * name that should match the setter. For instance if you have a dependency
  7720. * class called My_Mailer and a setter on the model called setMailSystem
  7721. * you should pass an instance of My_Mailer with key MailSystem.
  7722. * The injector will now look for a setter called setMailSystem.
  7723. *
  7724. * @param string $dependencyID name of the dependency (should match setter)
  7725. * @param mixed $dependency the service to be injected
  7726. */
  7727. public function addDependency($dependencyID,$dependency) {
  7728. $this->dependencies[$dependencyID] = $dependency;
  7729. }
  7730. /**
  7731. * Returns an instance of the class $modelClassName completely
  7732. * configured as far as possible with all the available
  7733. * service objects in the dependency list.
  7734. *
  7735. * @param string $modelClassName the name of the class of the model
  7736. *
  7737. * @return mixed $object the model/object
  7738. */
  7739. public function getInstance($modelClassName) {
  7740. $object = new $modelClassName;
  7741. if ($this->dependencies && is_array($this->dependencies)) {
  7742. foreach($this->dependencies as $key=>$dep) {
  7743. $depSetter = 'set'.$key;
  7744. if (method_exists($object,$depSetter)) {
  7745. $object->$depSetter($dep);
  7746. }
  7747. }
  7748. }
  7749. return $object;
  7750. }
  7751. }
  7752. /**
  7753. * RedBean Duplication Manager
  7754. *
  7755. * @file RedBean/DuplicationManager.php
  7756. * @description Creates deep copies of beans
  7757. *
  7758. * @author Gabor de Mooij and the RedBeanPHP Community
  7759. * @license BSD/GPLv2
  7760. *
  7761. * copyright (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community
  7762. * This source file is subject to the BSD/GPLv2 License that is bundled
  7763. * with this source code in the file license.txt.
  7764. *
  7765. */
  7766. class RedBean_DuplicationManager {
  7767. /**
  7768. * The Dup Manager requires a toolbox
  7769. * @var RedBean_Toolbox
  7770. */
  7771. protected $toolbox;
  7772. /**
  7773. * Association Manager
  7774. * @var RedBean_AssociationManager
  7775. */
  7776. protected $associationManager;
  7777. /**
  7778. * RedBeanPHP OODB instance
  7779. * @var RedBean_OODBBean
  7780. */
  7781. protected $redbean;
  7782. /**
  7783. * Constructor,
  7784. * creates a new instance of DupManager.
  7785. * @param RedBean_Toolbox $toolbox
  7786. */
  7787. public function __construct( RedBean_Toolbox $toolbox ) {
  7788. $this->toolbox = $toolbox;
  7789. $this->redbean = $toolbox->getRedBean();
  7790. $this->associationManager = $this->redbean->getAssociationManager();
  7791. }
  7792. /**
  7793. * Makes a copy of a bean. This method makes a deep copy
  7794. * of the bean.The copy will have the following features.
  7795. * - All beans in own-lists will be duplicated as well
  7796. * - All references to shared beans will be copied but not the shared beans themselves
  7797. * - All references to parent objects (_id fields) will be copied but not the parents themselves
  7798. * In most cases this is the desired scenario for copying beans.
  7799. * This function uses a trail-array to prevent infinite recursion, if a recursive bean is found
  7800. * (i.e. one that already has been processed) the ID of the bean will be returned.
  7801. * This should not happen though.
  7802. *
  7803. * Note:
  7804. * This function does a reflectional database query so it may be slow.
  7805. *
  7806. * Note:
  7807. * this function actually passes the arguments to a protected function called
  7808. * duplicate() that does all the work. This method takes care of creating a clone
  7809. * of the bean to avoid the bean getting tainted (triggering saving when storing it).
  7810. *
  7811. * @param RedBean_OODBBean $bean bean to be copied
  7812. * @param array $trail for internal usage, pass array()
  7813. * @param boolean $pid for internal usage
  7814. *
  7815. * @return array $copiedBean the duplicated bean
  7816. */
  7817. public function dup($bean,$trail=array(),$pid=false) {
  7818. $beanCopy = clone($bean);
  7819. return $this->duplicate($beanCopy,$trail,$pid);
  7820. }
  7821. /**
  7822. * Makes a copy of a bean. This method makes a deep copy
  7823. * of the bean.The copy will have the following features.
  7824. * - All beans in own-lists will be duplicated as well
  7825. * - All references to shared beans will be copied but not the shared beans themselves
  7826. * - All references to parent objects (_id fields) will be copied but not the parents themselves
  7827. * In most cases this is the desired scenario for copying beans.
  7828. * This function uses a trail-array to prevent infinite recursion, if a recursive bean is found
  7829. * (i.e. one that already has been processed) the ID of the bean will be returned.
  7830. * This should not happen though.
  7831. *
  7832. * Note:
  7833. * This function does a reflectional database query so it may be slow.
  7834. *
  7835. * @param RedBean_OODBBean $bean bean to be copied
  7836. * @param array $trail for internal usage, pass array()
  7837. * @param boolean $pid for internal usage
  7838. *
  7839. * @return array $copiedBean the duplicated bean
  7840. */
  7841. protected function duplicate($bean,$trail=array(),$pid=false) {
  7842. $type = $bean->getMeta('type');
  7843. $key = $type.$bean->getID();
  7844. if (isset($trail[$key])) return $bean;
  7845. $trail[$key]=$bean;
  7846. $copy =$this->redbean->dispense($type);
  7847. $copy->import( $bean->getProperties() );
  7848. $copy->id = 0;
  7849. $tables = $this->toolbox->getWriter()->getTables();
  7850. foreach($tables as $table) {
  7851. if (strpos($table,'_')!==false || $table==$type) continue;
  7852. $owned = 'own'.ucfirst($table);
  7853. $shared = 'shared'.ucfirst($table);
  7854. if ($beans = $bean->$owned) {
  7855. $copy->$owned = array();
  7856. foreach($beans as $subBean) {
  7857. array_push($copy->$owned,$this->duplicate($subBean,$trail,$pid));
  7858. }
  7859. }
  7860. $copy->setMeta('sys.shadow.'.$owned,null);
  7861. if ($beans = $bean->$shared) {
  7862. $copy->$shared = array();
  7863. foreach($beans as $subBean) {
  7864. array_push($copy->$shared,$subBean);
  7865. }
  7866. }
  7867. $copy->setMeta('sys.shadow.'.$shared,null);
  7868. }
  7869. if ($pid) $copy->id = $bean->id;
  7870. return $copy;
  7871. }
  7872. }
  7873. class R extends RedBean_Facade{
  7874. }