PageRenderTime 287ms CodeModel.GetById 47ms RepoModel.GetById 1ms app.codeStats 1ms

/redbean/rb.php

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