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