PageRenderTime 63ms CodeModel.GetById 25ms RepoModel.GetById 0ms app.codeStats 1ms

/framework/rb.php

https://github.com/jburns20/webapp2php
PHP | 9819 lines | 4159 code | 1224 blank | 4436 comment | 500 complexity | c2e4bfe6aea2c1e23fbb2c829b736c7b MD5 | raw file

Large files files are truncated, but you can click here to view the full file

  1. <?php
  2. namespace RedBeanPHP {
  3. /**
  4. * RedBean Logging interface.
  5. * Provides a uniform and convenient logging
  6. * interface throughout RedBeanPHP.
  7. *
  8. * @file RedBean/Logging.php
  9. * @desc Logging interface for RedBeanPHP ORM
  10. * @author Gabor de Mooij and the RedBeanPHP Community
  11. * @license BSD/GPLv2
  12. *
  13. * copyright (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community
  14. * This source file is subject to the BSD/GPLv2 License that is bundled
  15. * with this source code in the file license.txt.
  16. */
  17. interface Logger
  18. {
  19. /**
  20. * A logger (for\PDO or OCI driver) needs to implement the log method.
  21. * The log method will receive logging data. Note that the number of parameters is 0, this means
  22. * all parameters are optional and the number may vary. This way the logger can be used in a very
  23. * flexible way. Sometimes the logger is used to log a simple error message and in other
  24. * situations sql and bindings are passed.
  25. * The log method should be able to accept all kinds of parameters and data by using
  26. * functions like func_num_args/func_get_args.
  27. *
  28. * @return void
  29. */
  30. public function log();
  31. }
  32. }
  33. namespace RedBeanPHP\Logger {
  34. use RedBeanPHP\Logger as Logger;
  35. use RedBeanPHP\RedException as RedException;
  36. use RedBeanPHP\RedException\Security as Security;
  37. /**
  38. * Logger. Provides a basic logging function for RedBeanPHP.
  39. *
  40. * @file RedBean/Logger.php
  41. * @desc Logger
  42. * @author Gabor de Mooij and the RedBeanPHP Community
  43. * @license BSD/GPLv2
  44. *
  45. * Provides a basic logging function for RedBeanPHP.
  46. *
  47. * copyright (c) G.J.G.T. (Gabor) de Mooij
  48. * This source file is subject to the BSD/GPLv2 License that is bundled
  49. * with this source code in the file license.txt.
  50. */
  51. class RDefault implements Logger
  52. {
  53. /**
  54. * @var integer
  55. */
  56. protected $mode = 0;
  57. /**
  58. * @var array
  59. */
  60. protected $logs = array();
  61. /**
  62. * Default logger method logging to STDOUT.
  63. * This is the default/reference implementation of a logger.
  64. * This method will write the message value to STDOUT (screen).
  65. *
  66. * @param $message (optional)
  67. *
  68. * @return void
  69. */
  70. public function log()
  71. {
  72. if ( func_num_args() < 1 ) return;
  73. foreach ( func_get_args() as $argument ) {
  74. if ( is_array( $argument ) ) {
  75. $log = print_r( $argument, TRUE );
  76. if ( $this->mode === 0 ) {
  77. echo $log;
  78. } else {
  79. $this->logs[] = $log;
  80. }
  81. } else {
  82. if ( $this->mode === 0 ) {
  83. echo $argument;
  84. } else {
  85. $this->logs[] = $argument;
  86. }
  87. }
  88. if ($this->mode === 0) echo "<br>\n";
  89. }
  90. }
  91. /**
  92. * Returns the logs array.
  93. *
  94. * @return array
  95. */
  96. public function getLogs()
  97. {
  98. return $this->logs;
  99. }
  100. /**
  101. * Empties the logs array.
  102. *
  103. * @return self
  104. */
  105. public function clear()
  106. {
  107. $this->logs = array();
  108. return $this;
  109. }
  110. /**
  111. * Selects a logging mode.
  112. * Mode 0 means echoing all statements, while mode 1
  113. * means populating the logs array.
  114. *
  115. * @param integer $mode mode
  116. *
  117. * @return self
  118. */
  119. public function setMode( $mode )
  120. {
  121. if ($mode !== 0 && $mode !== 1) {
  122. throw new RedException( 'Invalid mode selected for logger, use 1 or 0.' );
  123. }
  124. $this->mode = $mode;
  125. return $this;
  126. }
  127. /**
  128. * Searches for all log entries in internal log array
  129. * for $needle and returns those entries.
  130. *
  131. * @param string $needle needle
  132. *
  133. * @return array
  134. */
  135. public function grep( $needle )
  136. {
  137. $found = array();
  138. foreach( $this->logs as $logEntry ) {
  139. if (strpos( $logEntry, $needle ) !== false) $found[] = $logEntry;
  140. }
  141. return $found;
  142. }
  143. }
  144. }
  145. namespace RedBeanPHP {
  146. /**
  147. * Interface for database drivers
  148. *
  149. * @file RedBean/Driver.php
  150. * @desc Describes the API for database classes
  151. * @author Gabor de Mooij and the RedBeanPHP Community
  152. * @license BSD/GPLv2
  153. *
  154. * The Driver API conforms to the ADODB pseudo standard
  155. * for database drivers.
  156. *
  157. * copyright (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community
  158. * This source file is subject to the BSD/GPLv2 License that is bundled
  159. * with this source code in the file license.txt.
  160. */
  161. interface Driver
  162. {
  163. /**
  164. * Runs a query and fetches results as a multi dimensional array.
  165. *
  166. * @param string $sql SQL to be executed
  167. * @param array $bindings list of values to bind to SQL snippet
  168. *
  169. * @return array
  170. */
  171. public function GetAll( $sql, $bindings = array() );
  172. /**
  173. * Runs a query and fetches results as a column.
  174. *
  175. * @param string $sql SQL Code to execute
  176. * @param array $bindings list of values to bind to SQL snippet
  177. *
  178. * @return array
  179. */
  180. public function GetCol( $sql, $bindings = array() );
  181. /**
  182. * Runs a query and returns results as a single cell.
  183. *
  184. * @param string $sql SQL to execute
  185. * @param array $bindings list of values to bind to SQL snippet
  186. *
  187. * @return mixed
  188. */
  189. public function GetCell( $sql, $bindings = array() );
  190. /**
  191. * Runs a query and returns results as an associative array
  192. * indexed by the first column.
  193. *
  194. * @param string $sql SQL to execute
  195. * @param array $bindings list of values to bind to SQL snippet
  196. *
  197. * @return mixed
  198. */
  199. public function GetAssocRow( $sql, $bindings = array() );
  200. /**
  201. * Runs a query and returns a flat array containing the values of
  202. * one row.
  203. *
  204. * @param string $sql SQL to execute
  205. * @param array $bindings list of values to bind to SQL snippet
  206. *
  207. * @return array
  208. */
  209. public function GetRow( $sql, $bindings = array() );
  210. /**
  211. * Executes SQL code and allows key-value binding.
  212. * This function allows you to provide an array with values to bind
  213. * to query parameters. For instance you can bind values to question
  214. * marks in the query. Each value in the array corresponds to the
  215. * question mark in the query that matches the position of the value in the
  216. * array. You can also bind values using explicit keys, for instance
  217. * array(":key"=>123) will bind the integer 123 to the key :key in the
  218. * SQL. This method has no return value.
  219. *
  220. * @param string $sql SQL Code to execute
  221. * @param array $bindings list of values to bind to SQL snippet
  222. *
  223. * @return array Affected Rows
  224. */
  225. public function Execute( $sql, $bindings = array() );
  226. /**
  227. * Returns the latest insert ID if driver does support this
  228. * feature.
  229. *
  230. * @return integer
  231. */
  232. public function GetInsertID();
  233. /**
  234. * Returns the number of rows affected by the most recent query
  235. * if the currently selected driver driver supports this feature.
  236. *
  237. * @return integer
  238. */
  239. public function Affected_Rows();
  240. /**
  241. * Toggles debug mode. In debug mode the driver will print all
  242. * SQL to the screen together with some information about the
  243. * results. All SQL code that passes through the driver will be
  244. * passes on to the screen for inspection.
  245. * This method has no return value.
  246. *
  247. * @param boolean $trueFalse turn on/off
  248. *
  249. * @return void
  250. */
  251. public function setDebugMode( $tf );
  252. /**
  253. * Starts a transaction.
  254. *
  255. * @return void
  256. */
  257. public function CommitTrans();
  258. /**
  259. * Commits a transaction.
  260. *
  261. * @return void
  262. */
  263. public function StartTrans();
  264. /**
  265. * Rolls back a transaction.
  266. *
  267. * @return void
  268. */
  269. public function FailTrans();
  270. }
  271. }
  272. namespace RedBeanPHP\Driver {
  273. use RedBeanPHP\Driver as Driver;
  274. use RedBeanPHP\Logger as Logger;
  275. use RedBeanPHP\QueryWriter\AQueryWriter as AQueryWriter;
  276. use RedBeanPHP\RedException\SQL as SQL;
  277. use RedBeanPHP\Logger\RDefault as RDefault;
  278. use RedBeanPHP\PDOCompatible as PDOCompatible;
  279. /**
  280. *\PDO Driver
  281. * This Driver implements the RedBean Driver API
  282. *
  283. * @file RedBean/PDO.php
  284. * @desc \PDO Driver
  285. * @author Gabor de Mooij and the RedBeanPHP Community, Desfrenes
  286. * @license BSD/GPLv2
  287. *
  288. * (c) copyright Desfrenes & Gabor de Mooij and the RedBeanPHP community
  289. * This source file is subject to the BSD/GPLv2 License that is bundled
  290. * with this source code in the file license.txt.
  291. */
  292. class RPDO implements Driver
  293. {
  294. /**
  295. * @var string
  296. */
  297. protected $dsn;
  298. /**
  299. * @var boolean
  300. */
  301. protected $debug = FALSE;
  302. /**
  303. * @var Logger
  304. */
  305. protected $logger = NULL;
  306. /**
  307. * @var\PDO
  308. */
  309. protected $pdo;
  310. /**
  311. * @var integer
  312. */
  313. protected $affectedRows;
  314. /**
  315. * @var integer
  316. */
  317. protected $resultArray;
  318. /**
  319. * @var array
  320. */
  321. protected $connectInfo = array();
  322. /**
  323. * @var boolean
  324. */
  325. protected $isConnected = FALSE;
  326. /**
  327. * @var bool
  328. */
  329. protected $flagUseStringOnlyBinding = FALSE;
  330. /**
  331. * @var string
  332. */
  333. protected $mysqlEncoding = '';
  334. /**
  335. * Binds parameters. This method binds parameters to a\PDOStatement for
  336. * Query Execution. This method binds parameters as NULL, INTEGER or STRING
  337. * and supports both named keys and question mark keys.
  338. *
  339. * @param \PDOStatement $statement \PDO Statement instance
  340. * @param array $bindings values that need to get bound to the statement
  341. *
  342. * @return void
  343. */
  344. protected function bindParams( $statement, $bindings )
  345. {
  346. foreach ( $bindings as $key => &$value ) {
  347. if ( is_integer( $key ) ) {
  348. if ( is_null( $value ) ) {
  349. $statement->bindValue( $key + 1, NULL,\PDO::PARAM_NULL );
  350. } elseif ( !$this->flagUseStringOnlyBinding && AQueryWriter::canBeTreatedAsInt( $value ) && $value < 2147483648 ) {
  351. $statement->bindParam( $key + 1, $value,\PDO::PARAM_INT );
  352. } else {
  353. $statement->bindParam( $key + 1, $value,\PDO::PARAM_STR );
  354. }
  355. } else {
  356. if ( is_null( $value ) ) {
  357. $statement->bindValue( $key, NULL,\PDO::PARAM_NULL );
  358. } elseif ( !$this->flagUseStringOnlyBinding && AQueryWriter::canBeTreatedAsInt( $value ) && $value < 2147483648 ) {
  359. $statement->bindParam( $key, $value,\PDO::PARAM_INT );
  360. } else {
  361. $statement->bindParam( $key, $value,\PDO::PARAM_STR );
  362. }
  363. }
  364. }
  365. }
  366. /**
  367. * This method runs the actual SQL query and binds a list of parameters to the query.
  368. * slots. The result of the query will be stored in the protected property
  369. * $rs (always array). The number of rows affected (result of rowcount, if supported by database)
  370. * is stored in protected property $affectedRows. If the debug flag is set
  371. * this function will send debugging output to screen buffer.
  372. *
  373. * @param string $sql the SQL string to be send to database server
  374. * @param array $bindings the values that need to get bound to the query slots
  375. *
  376. * @return void
  377. *
  378. * @throws SQL
  379. */
  380. protected function runQuery( $sql, $bindings, $options = array() )
  381. {
  382. $this->connect();
  383. if ( $this->debug && $this->logger ) {
  384. $this->logger->log( $sql, $bindings );
  385. }
  386. try {
  387. if ( strpos( 'pgsql', $this->dsn ) === 0 ) {
  388. $statement = $this->pdo->prepare( $sql, array(\PDO::PGSQL_ATTR_DISABLE_NATIVE_PREPARED_STATEMENT => TRUE ) );
  389. } else {
  390. $statement = $this->pdo->prepare( $sql );
  391. }
  392. $this->bindParams( $statement, $bindings );
  393. $statement->execute();
  394. $this->affectedRows = $statement->rowCount();
  395. if ( $statement->columnCount() ) {
  396. $fetchStyle = ( isset( $options['fetchStyle'] ) ) ? $options['fetchStyle'] : NULL;
  397. $this->resultArray = $statement->fetchAll( $fetchStyle );
  398. if ( $this->debug && $this->logger ) {
  399. $this->logger->log( 'resultset: ' . count( $this->resultArray ) . ' rows' );
  400. }
  401. } else {
  402. $this->resultArray = array();
  403. }
  404. } catch (\PDOException $e ) {
  405. //Unfortunately the code field is supposed to be int by default (php)
  406. //So we need a property to convey the SQL State code.
  407. $err = $e->getMessage();
  408. if ( $this->debug && $this->logger ) $this->logger->log( 'An error occurred: ' . $err );
  409. $exception = new SQL( $err, 0 );
  410. $exception->setSQLState( $e->getCode() );
  411. throw $exception;
  412. }
  413. }
  414. /**
  415. * Try to fix MySQL character encoding problems.
  416. * MySQL < 5.5 does not support proper 4 byte unicode but they
  417. * seem to have added it with version 5.5 under a different label: utf8mb4.
  418. * We try to select the best possible charset based on your version data.
  419. */
  420. protected function setEncoding()
  421. {
  422. $driver = $this->pdo->getAttribute(\PDO::ATTR_DRIVER_NAME );
  423. $version = floatval( $this->pdo->getAttribute(\PDO::ATTR_SERVER_VERSION ) );
  424. if ($driver === 'mysql') {
  425. $encoding = ($version >= 5.5) ? 'utf8mb4' : 'utf8';
  426. $this->pdo->setAttribute(\PDO::MYSQL_ATTR_INIT_COMMAND, 'SET NAMES '.$encoding ); //on every re-connect
  427. $this->pdo->exec(' SET NAMES '. $encoding); //also for current connection
  428. $this->mysqlEncoding = $encoding;
  429. }
  430. }
  431. /**
  432. * Returns the best possible encoding for MySQL based on version data.
  433. *
  434. * @return string
  435. */
  436. public function getMysqlEncoding()
  437. {
  438. return $this->mysqlEncoding;
  439. }
  440. /**
  441. * Constructor. You may either specify dsn, user and password or
  442. * just give an existing\PDO connection.
  443. * Examples:
  444. * $driver = new RPDO($dsn, $user, $password);
  445. * $driver = new RPDO($existingConnection);
  446. *
  447. * @param string|object $dsn database connection string
  448. * @param string $user optional, usename to sign in
  449. * @param string $pass optional, password for connection login
  450. *
  451. */
  452. public function __construct( $dsn, $user = NULL, $pass = NULL )
  453. {
  454. if ( is_object( $dsn ) ) {
  455. $this->pdo = $dsn;
  456. $this->isConnected = TRUE;
  457. $this->setEncoding();
  458. $this->pdo->setAttribute(\PDO::ATTR_ERRMODE,\PDO::ERRMODE_EXCEPTION );
  459. $this->pdo->setAttribute(\PDO::ATTR_DEFAULT_FETCH_MODE,\PDO::FETCH_ASSOC );
  460. // make sure that the dsn at least contains the type
  461. $this->dsn = $this->getDatabaseType();
  462. } else {
  463. $this->dsn = $dsn;
  464. $this->connectInfo = array( 'pass' => $pass, 'user' => $user );
  465. }
  466. }
  467. /**
  468. * Whether to bind all parameters as strings.
  469. *
  470. * @param boolean $yesNo pass TRUE to bind all parameters as strings.
  471. *
  472. * @return void
  473. */
  474. public function setUseStringOnlyBinding( $yesNo )
  475. {
  476. $this->flagUseStringOnlyBinding = (boolean) $yesNo;
  477. }
  478. /**
  479. * Establishes a connection to the database using PHP\PDO
  480. * functionality. If a connection has already been established this
  481. * method will simply return directly. This method also turns on
  482. * UTF8 for the database and\PDO-ERRMODE-EXCEPTION as well as
  483. *\PDO-FETCH-ASSOC.
  484. *
  485. * @throws\PDOException
  486. *
  487. * @return void
  488. */
  489. public function connect()
  490. {
  491. if ( $this->isConnected ) return;
  492. try {
  493. $user = $this->connectInfo['user'];
  494. $pass = $this->connectInfo['pass'];
  495. $this->pdo = new\PDO(
  496. $this->dsn,
  497. $user,
  498. $pass,
  499. array(\PDO::ATTR_ERRMODE =>\PDO::ERRMODE_EXCEPTION,
  500. \PDO::ATTR_DEFAULT_FETCH_MODE =>\PDO::FETCH_ASSOC,
  501. )
  502. );
  503. $this->setEncoding();
  504. $this->pdo->setAttribute(\PDO::ATTR_STRINGIFY_FETCHES, TRUE );
  505. $this->isConnected = TRUE;
  506. } catch (\PDOException $exception ) {
  507. $matches = array();
  508. $dbname = ( preg_match( '/dbname=(\w+)/', $this->dsn, $matches ) ) ? $matches[1] : '?';
  509. throw new\PDOException( 'Could not connect to database (' . $dbname . ').', $exception->getCode() );
  510. }
  511. }
  512. /**
  513. * @see Driver::GetAll
  514. */
  515. public function GetAll( $sql, $bindings = array() )
  516. {
  517. $this->runQuery( $sql, $bindings );
  518. return $this->resultArray;
  519. }
  520. /**
  521. * @see Driver::GetAssocRow
  522. */
  523. public function GetAssocRow( $sql, $bindings = array() )
  524. {
  525. $this->runQuery( $sql, $bindings, array(
  526. 'fetchStyle' => \PDO::FETCH_ASSOC
  527. )
  528. );
  529. return $this->resultArray;
  530. }
  531. /**
  532. * @see Driver::GetCol
  533. */
  534. public function GetCol( $sql, $bindings = array() )
  535. {
  536. $rows = $this->GetAll( $sql, $bindings );
  537. $cols = array();
  538. if ( $rows && is_array( $rows ) && count( $rows ) > 0 ) {
  539. foreach ( $rows as $row ) {
  540. $cols[] = array_shift( $row );
  541. }
  542. }
  543. return $cols;
  544. }
  545. /**
  546. * @see Driver::GetCell
  547. */
  548. public function GetCell( $sql, $bindings = array() )
  549. {
  550. $arr = $this->GetAll( $sql, $bindings );
  551. $row1 = array_shift( $arr );
  552. $col1 = array_shift( $row1 );
  553. return $col1;
  554. }
  555. /**
  556. * @see Driver::GetRow
  557. */
  558. public function GetRow( $sql, $bindings = array() )
  559. {
  560. $arr = $this->GetAll( $sql, $bindings );
  561. return array_shift( $arr );
  562. }
  563. /**
  564. * @see Driver::Excecute
  565. */
  566. public function Execute( $sql, $bindings = array() )
  567. {
  568. $this->runQuery( $sql, $bindings );
  569. return $this->affectedRows;
  570. }
  571. /**
  572. * @see Driver::GetInsertID
  573. */
  574. public function GetInsertID()
  575. {
  576. $this->connect();
  577. return (int) $this->pdo->lastInsertId();
  578. }
  579. /**
  580. * @see Driver::Affected_Rows
  581. */
  582. public function Affected_Rows()
  583. {
  584. $this->connect();
  585. return (int) $this->affectedRows;
  586. }
  587. /**
  588. * Toggles debug mode. In debug mode the driver will print all
  589. * SQL to the screen together with some information about the
  590. * results.
  591. *
  592. * @param boolean $trueFalse turn on/off
  593. * @param Logger $logger logger instance
  594. *
  595. * @return void
  596. */
  597. public function setDebugMode( $tf, $logger = NULL )
  598. {
  599. $this->connect();
  600. $this->debug = (bool) $tf;
  601. if ( $this->debug and !$logger ) {
  602. $logger = new RDefault();
  603. }
  604. $this->setLogger( $logger );
  605. }
  606. /**
  607. * Injects Logger object.
  608. * Sets the logger instance you wish to use.
  609. *
  610. * @param Logger $logger the logger instance to be used for logging
  611. */
  612. public function setLogger( Logger $logger )
  613. {
  614. $this->logger = $logger;
  615. }
  616. /**
  617. * Gets Logger object.
  618. * Returns the currently active Logger instance.
  619. *
  620. * @return Logger
  621. */
  622. public function getLogger()
  623. {
  624. return $this->logger;
  625. }
  626. /**
  627. * @see Driver::StartTrans
  628. */
  629. public function StartTrans()
  630. {
  631. $this->connect();
  632. $this->pdo->beginTransaction();
  633. }
  634. /**
  635. * @see Driver::CommitTrans
  636. */
  637. public function CommitTrans()
  638. {
  639. $this->connect();
  640. $this->pdo->commit();
  641. }
  642. /**
  643. * @see Driver::FailTrans
  644. */
  645. public function FailTrans()
  646. {
  647. $this->connect();
  648. $this->pdo->rollback();
  649. }
  650. /**
  651. * Returns the name of database driver for\PDO.
  652. * Uses the\PDO attribute DRIVER NAME to obtain the name of the
  653. *\PDO driver.
  654. *
  655. * @return string
  656. */
  657. public function getDatabaseType()
  658. {
  659. $this->connect();
  660. return $this->pdo->getAttribute(\PDO::ATTR_DRIVER_NAME );
  661. }
  662. /**
  663. * Returns the version number of the database.
  664. *
  665. * @return mixed $version version number of the database
  666. */
  667. public function getDatabaseVersion()
  668. {
  669. $this->connect();
  670. return $this->pdo->getAttribute(\PDO::ATTR_CLIENT_VERSION );
  671. }
  672. /**
  673. * Returns the underlying PHP\PDO instance.
  674. *
  675. * @return\PDO
  676. */
  677. public function getPDO()
  678. {
  679. $this->connect();
  680. return $this->pdo;
  681. }
  682. /**
  683. * Closes database connection by destructing\PDO.
  684. *
  685. * @return void
  686. */
  687. public function close()
  688. {
  689. $this->pdo = NULL;
  690. $this->isConnected = FALSE;
  691. }
  692. /**
  693. * Returns TRUE if the current\PDO instance is connected.
  694. *
  695. * @return boolean
  696. */
  697. public function isConnected()
  698. {
  699. return $this->isConnected && $this->pdo;
  700. }
  701. }
  702. }
  703. namespace RedBeanPHP {
  704. use RedBeanPHP\QueryWriter\AQueryWriter as AQueryWriter;
  705. use RedBeanPHP\BeanHelper as BeanHelper;
  706. use RedBeanPHP\RedException\Security as Security;
  707. /**
  708. * OODBBean (Object Oriented DataBase Bean)
  709. *
  710. * @file RedBean/OODBBean.php
  711. * @desc The Bean class used for passing information
  712. * @author Gabor de Mooij and the RedBeanPHP community
  713. * @license BSD/GPLv2
  714. *
  715. * copyright (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community.
  716. * This source file is subject to the BSD/GPLv2 License that is bundled
  717. * with this source code in the file license.txt.
  718. */
  719. class OODBBean implements\IteratorAggregate,\ArrayAccess,\Countable
  720. {
  721. /**
  722. * Whether to skip beautification of columns or not.
  723. *
  724. * @var boolean
  725. */
  726. private $flagSkipBeau = FALSE;
  727. /**
  728. * This is where the real properties of the bean live. They are stored and retrieved
  729. * by the magic getter and setter (__get and __set).
  730. *
  731. * @var array $properties
  732. */
  733. private $properties = array();
  734. /**
  735. * Here we keep the meta data of a bean.
  736. *
  737. * @var array
  738. */
  739. private $__info = array();
  740. /**
  741. * The BeanHelper allows the bean to access the toolbox objects to implement
  742. * rich functionality, otherwise you would have to do everything with R or
  743. * external objects.
  744. *
  745. * @var BeanHelper
  746. */
  747. private $beanHelper = NULL;
  748. /**
  749. * @var null
  750. */
  751. private $fetchType = NULL;
  752. /**
  753. * @var string
  754. */
  755. private $withSql = '';
  756. /**
  757. * @var array
  758. */
  759. private $withParams = array();
  760. /**
  761. * @var string
  762. */
  763. private $aliasName = NULL;
  764. /**
  765. * @var string
  766. */
  767. private $via = NULL;
  768. /**
  769. * @var boolean
  770. */
  771. private $noLoad = FALSE;
  772. /**
  773. * @var boolean
  774. */
  775. private $all = FALSE;
  776. /** Returns the alias for a type
  777. *
  778. * @param string $type type
  779. *
  780. * @return string $type type
  781. */
  782. private function getAlias( $type )
  783. {
  784. if ( $this->fetchType ) {
  785. $type = $this->fetchType;
  786. $this->fetchType = NULL;
  787. }
  788. return $type;
  789. }
  790. /**
  791. * Internal method.
  792. * Obtains a shared list for a certain type.
  793. *
  794. * @param string $type the name of the list you want to retrieve.
  795. *
  796. * @return array
  797. */
  798. private function getSharedList( $type, $redbean, $toolbox )
  799. {
  800. $writer = $toolbox->getWriter();
  801. if ( $this->via ) {
  802. $oldName = $writer->getAssocTable( array( $this->__info['type'], $type ) );
  803. if ( $oldName !== $this->via ) {
  804. //set the new renaming rule
  805. $writer->renameAssocTable( $oldName, $this->via );
  806. }
  807. $this->via = NULL;
  808. }
  809. $beans = array();
  810. if ($this->getID()) {
  811. $type = $this->beau( $type );
  812. $assocManager = $redbean->getAssociationManager();
  813. $beans = $assocManager->related( $this, $type, $this->withSql, $this->withParams );
  814. }
  815. $this->withSql = '';
  816. $this->withParams = array();
  817. return $beans;
  818. }
  819. /**
  820. * Internal method.
  821. * Obtains the own list of a certain type.
  822. *
  823. * @param string $type name of the list you want to retrieve
  824. *
  825. * @return array
  826. */
  827. private function getOwnList( $type, $redbean )
  828. {
  829. $type = $this->beau( $type );
  830. if ( $this->aliasName ) {
  831. $parentField = $this->aliasName;
  832. $myFieldLink = $parentField . '_id';
  833. $this->__info['sys.alias.' . $type] = $this->aliasName;
  834. $this->aliasName = NULL;
  835. } else {
  836. $parentField = $this->__info['type'];
  837. $myFieldLink = $parentField . '_id';
  838. }
  839. $beans = array();
  840. if ( $this->getID() ) {
  841. $firstKey = NULL;
  842. if ( count( $this->withParams ) > 0 ) {
  843. reset( $this->withParams );
  844. $firstKey = key( $this->withParams );
  845. }
  846. if ( !is_numeric( $firstKey ) || $firstKey === NULL ) {
  847. $bindings = $this->withParams;
  848. $bindings[':slot0'] = $this->getID();
  849. $beans = $redbean->find( $type, array(), " $myFieldLink = :slot0 " . $this->withSql, $bindings );
  850. } else {
  851. $bindings = array_merge( array( $this->getID() ), $this->withParams );
  852. $beans = $redbean->find( $type, array(), " $myFieldLink = ? " . $this->withSql, $bindings );
  853. }
  854. }
  855. $this->withSql = '';
  856. $this->withParams = array();
  857. foreach ( $beans as $beanFromList ) {
  858. $beanFromList->__info['sys.parentcache.' . $parentField] = $this;
  859. }
  860. return $beans;
  861. }
  862. /**
  863. * Sets a meta property for all beans. This is a quicker way to set
  864. * the meta properties for a collection of beans because this method
  865. * can directly access the property arrays of the beans.
  866. * This method returns the beans.
  867. *
  868. * @param array $beans beans to set the meta property of
  869. * @param string $property property to set
  870. * @param mixed $value value
  871. *
  872. * @return array
  873. */
  874. public static function setMetaAll( $beans, $property, $value )
  875. {
  876. foreach( $beans as $bean ) {
  877. $bean->__info[ $property ] = $value;
  878. }
  879. return $beans;
  880. }
  881. /**
  882. * Initializes a bean. Used by OODB for dispensing beans.
  883. * It is not recommended to use this method to initialize beans. Instead
  884. * use the OODB object to dispense new beans. You can use this method
  885. * if you build your own bean dispensing mechanism.
  886. *
  887. * @param string $type type of the new bean
  888. * @param BeanHelper $beanhelper bean helper to obtain a toolbox and a model
  889. *
  890. * @return void
  891. */
  892. public function initializeForDispense( $type, BeanHelper $beanhelper )
  893. {
  894. $this->beanHelper = $beanhelper;
  895. $this->__info['type'] = $type;
  896. $this->__info['sys.id'] = 'id';
  897. $this->__info['sys.orig'] = array( 'id' => 0 );
  898. $this->__info['tainted'] = TRUE;
  899. $this->properties['id'] = 0;
  900. }
  901. /**
  902. * Sets the Bean Helper. Normally the Bean Helper is set by OODB.
  903. * Here you can change the Bean Helper. The Bean Helper is an object
  904. * providing access to a toolbox for the bean necessary to retrieve
  905. * nested beans (bean lists: ownBean, sharedBean) without the need to
  906. * rely on static calls to the facade (or make this class dep. on OODB).
  907. *
  908. * @param BeanHelper $helper
  909. *
  910. * @return void
  911. */
  912. public function setBeanHelper( BeanHelper $helper )
  913. {
  914. $this->beanHelper = $helper;
  915. }
  916. /**
  917. * Returns an\ArrayIterator so you can treat the bean like
  918. * an array with the properties container as its contents.
  919. * This method is meant for PHP and allows you to access beans as if
  920. * they were arrays, i.e. using array notation:
  921. *
  922. * $bean[ $key ] = $value;
  923. *
  924. * Note that not all PHP functions work with the array interface.
  925. *
  926. * @return\ArrayIterator
  927. */
  928. public function getIterator()
  929. {
  930. return new\ArrayIterator( $this->properties );
  931. }
  932. /**
  933. * Imports all values from an associative array $array. Chainable.
  934. * This method imports the values in the first argument as bean
  935. * propery and value pairs. Use the second parameter to provide a
  936. * selection. If a selection array is passed, only the entries
  937. * having keys mentioned in the selection array will be imported.
  938. * Set the third parameter to TRUE to preserve spaces in selection keys.
  939. *
  940. * @param array $array what you want to import
  941. * @param string|array $selection selection of values
  942. * @param boolean $notrim if TRUE selection keys will NOT be trimmed
  943. *
  944. * @return OODBBean
  945. */
  946. public function import( $array, $selection = FALSE, $notrim = FALSE )
  947. {
  948. if ( is_string( $selection ) ) {
  949. $selection = explode( ',', $selection );
  950. }
  951. if ( !$notrim && is_array( $selection ) ) {
  952. foreach ( $selection as $key => $selected ) {
  953. $selection[$key] = trim( $selected );
  954. }
  955. }
  956. foreach ( $array as $key => $value ) {
  957. if ( $key != '__info' ) {
  958. if ( !$selection || ( $selection && in_array( $key, $selection ) ) ) {
  959. if ( is_array($value ) ) {
  960. if ( isset( $value['_type'] ) ) {
  961. $bean = $this->beanHelper->getToolbox()->getRedBean()->dispense( $value['_type'] );
  962. unset( $value['_type'] );
  963. $bean->import($value);
  964. $this->$key = $bean;
  965. } else {
  966. $listBeans = array();
  967. foreach( $value as $listKey => $listItem ) {
  968. $bean = $this->beanHelper->getToolbox()->getRedBean()->dispense( $listItem['_type'] );
  969. unset( $listItem['_type'] );
  970. $bean->import($listItem);
  971. $list = &$this->$key;
  972. $list[ $listKey ] = $bean;
  973. }
  974. }
  975. } else {
  976. $this->$key = $value;
  977. }
  978. }
  979. }
  980. }
  981. return $this;
  982. }
  983. /**
  984. * Fast way to import a row.
  985. * Does not perform any checks.
  986. *
  987. * @param array $row a database row
  988. *
  989. * @return self
  990. */
  991. public function importRow( $row )
  992. {
  993. $this->properties = $row;
  994. $this->__info['sys.orig'] = $row;
  995. return $this;
  996. }
  997. /**
  998. * Imports data from another bean. Chainable.
  999. * Copies the properties from the source bean to the internal
  1000. * property list.
  1001. *
  1002. * @param OODBBean $sourceBean the source bean to take properties from
  1003. *
  1004. * @return OODBBean
  1005. */
  1006. public function importFrom( OODBBean $sourceBean )
  1007. {
  1008. $this->__info['tainted'] = TRUE;
  1009. $this->properties = $sourceBean->properties;
  1010. return $this;
  1011. }
  1012. /**
  1013. * Injects the properties of another bean but keeps the original ID.
  1014. * Just like import() but keeps the original ID.
  1015. * Chainable.
  1016. *
  1017. * @param OODBBean $otherBean the bean whose properties you would like to copy
  1018. *
  1019. * @return OODBBean
  1020. */
  1021. public function inject( OODBBean $otherBean )
  1022. {
  1023. $myID = $this->properties['id'];
  1024. $this->import( $otherBean->export() );
  1025. $this->id = $myID;
  1026. return $this;
  1027. }
  1028. /**
  1029. * Exports the bean as an array.
  1030. * This function exports the contents of a bean to an array and returns
  1031. * the resulting array.
  1032. *
  1033. * @param boolean $meta set to TRUE if you want to export meta data as well
  1034. * @param boolean $parents set to TRUE if you want to export parents as well
  1035. * @param boolean $onlyMe set to TRUE if you want to export only this bean
  1036. * @param array $filters optional whitelist for export
  1037. *
  1038. * @return array
  1039. */
  1040. public function export( $meta = FALSE, $parents = FALSE, $onlyMe = FALSE, $filters = array() )
  1041. {
  1042. $arr = array();
  1043. if ( $parents ) {
  1044. foreach ( $this as $key => $value ) {
  1045. if ( substr( $key, -3 ) != '_id' ) continue;
  1046. $prop = substr( $key, 0, strlen( $key ) - 3 );
  1047. $this->$prop;
  1048. }
  1049. }
  1050. $hasFilters = is_array( $filters ) && count( $filters );
  1051. foreach ( $this as $key => $value ) {
  1052. if ( !$onlyMe && is_array( $value ) ) {
  1053. $vn = array();
  1054. foreach ( $value as $i => $b ) {
  1055. $vn[] = $b->export( $meta, FALSE, FALSE, $filters );
  1056. $value = $vn;
  1057. }
  1058. } elseif ( $value instanceof OODBBean ) {
  1059. if ( $hasFilters ) {
  1060. if ( !in_array( strtolower( $value->getMeta( 'type' ) ), $filters ) ) continue;
  1061. }
  1062. $value = $value->export( $meta, $parents, FALSE, $filters );
  1063. }
  1064. $arr[$key] = $value;
  1065. }
  1066. if ( $meta ) {
  1067. $arr['__info'] = $this->__info;
  1068. }
  1069. return $arr;
  1070. }
  1071. /**
  1072. * Implements isset() function for use as an array.
  1073. *
  1074. * @param string $property name of the property you want to check
  1075. *
  1076. * @return boolean
  1077. */
  1078. public function __isset( $property )
  1079. {
  1080. $property = $this->beau( $property );
  1081. if ( strpos( $property, 'xown' ) === 0 && ctype_upper( substr( $property, 4, 1 ) ) ) {
  1082. $property = substr($property, 1);
  1083. }
  1084. return isset( $this->properties[$property] );
  1085. }
  1086. /**
  1087. * Returns the ID of the bean no matter what the ID field is.
  1088. *
  1089. * @return string|null
  1090. */
  1091. public function getID()
  1092. {
  1093. return ( isset( $this->properties['id'] ) ) ? (string) $this->properties['id'] : NULL;
  1094. }
  1095. /**
  1096. * Unsets a property. This method will load the property first using
  1097. * __get.
  1098. *
  1099. * @param string $property property
  1100. *
  1101. * @return void
  1102. */
  1103. public function __unset( $property )
  1104. {
  1105. $property = $this->beau( $property );
  1106. if ( strpos( $property, 'xown' ) === 0 && ctype_upper( substr( $property, 4, 1 ) ) ) {
  1107. $property = substr($property, 1);
  1108. }
  1109. unset( $this->properties[$property] );
  1110. $shadowKey = 'sys.shadow.'.$property;
  1111. if ( isset( $this->__info[ $shadowKey ] ) ) unset( $this->__info[$shadowKey] );
  1112. //also clear modifiers
  1113. $this->withSql = '';
  1114. $this->withParams = array();
  1115. $this->aliasName = NULL;
  1116. $this->fetchType = NULL;
  1117. $this->noLoad = FALSE;
  1118. $this->all = FALSE;
  1119. $this->via = NULL;
  1120. return;
  1121. }
  1122. /**
  1123. * Adds WHERE clause conditions to ownList retrieval.
  1124. * For instance to get the pages that belong to a book you would
  1125. * issue the following command: $book->ownPage
  1126. * However, to order these pages by number use:
  1127. *
  1128. * $book->with(' ORDER BY `number` ASC ')->ownPage
  1129. *
  1130. * the additional SQL snippet will be merged into the final
  1131. * query.
  1132. *
  1133. * @param string $sql SQL to be added to retrieval query.
  1134. * @param array $bindings array with parameters to bind to SQL snippet
  1135. *
  1136. * @return OODBBean
  1137. */
  1138. public function with( $sql, $bindings = array() )
  1139. {
  1140. $this->withSql = $sql;
  1141. $this->withParams = $bindings;
  1142. return $this;
  1143. }
  1144. /**
  1145. * Just like with(). Except that this method prepends the SQL query snippet
  1146. * with AND which makes it slightly more comfortable to use a conditional
  1147. * SQL snippet. For instance to filter an own-list with pages (belonging to
  1148. * a book) on specific chapters you can use:
  1149. *
  1150. * $book->withCondition(' chapter = 3 ')->ownPage
  1151. *
  1152. * This will return in the own list only the pages having 'chapter == 3'.
  1153. *
  1154. * @param string $sql SQL to be added to retrieval query (prefixed by AND)
  1155. * @param array $bindings array with parameters to bind to SQL snippet
  1156. *
  1157. * @return OODBBean
  1158. */
  1159. public function withCondition( $sql, $bindings = array() )
  1160. {
  1161. $this->withSql = ' AND ' . $sql;
  1162. $this->withParams = $bindings;
  1163. return $this;
  1164. }
  1165. /**
  1166. * When prefix for a list, this causes the list to reload.
  1167. *
  1168. * @return self
  1169. */
  1170. public function all()
  1171. {
  1172. $this->all = TRUE;
  1173. return $this;
  1174. }
  1175. /**
  1176. * Tells the bean to only access the list but not load
  1177. * its contents. Use this if you only want to add something to a list
  1178. * and you have no interest in retrieving its contents from the database.
  1179. *
  1180. * @return self
  1181. */
  1182. public function noLoad()
  1183. {
  1184. $this->noLoad = TRUE;
  1185. return $this;
  1186. }
  1187. /**
  1188. * Prepares an own-list to use an alias. This is best explained using
  1189. * an example. Imagine a project and a person. The project always involves
  1190. * two persons: a teacher and a student. The person beans have been aliased in this
  1191. * case, so to the project has a teacher_id pointing to a person, and a student_id
  1192. * also pointing to a person. Given a project, we obtain the teacher like this:
  1193. *
  1194. * $project->fetchAs('person')->teacher;
  1195. *
  1196. * Now, if we want all projects of a teacher we cant say:
  1197. *
  1198. * $teacher->ownProject
  1199. *
  1200. * because the $teacher is a bean of type 'person' and no project has been
  1201. * assigned to a person. Instead we use the alias() method like this:
  1202. *
  1203. * $teacher->alias('teacher')->ownProject
  1204. *
  1205. * now we get the projects associated with the person bean aliased as
  1206. * a teacher.
  1207. *
  1208. * @param string $aliasName the alias name to use
  1209. *
  1210. * @return OODBBean
  1211. */
  1212. public function alias( $aliasName )
  1213. {
  1214. $this->aliasName = $this->beau( $aliasName );
  1215. return $this;
  1216. }
  1217. /**
  1218. * Returns properties of bean as an array.
  1219. * This method returns the raw internal property list of the
  1220. * bean. Only use this method for optimization purposes. Otherwise
  1221. * use the export() method to export bean data to arrays.
  1222. *
  1223. * @return array
  1224. */
  1225. public function getProperties()
  1226. {
  1227. return $this->properties;
  1228. }
  1229. /**
  1230. * Returns properties of bean as an array.
  1231. * This method returns the raw internal property list of the
  1232. * bean. Only use this method for optimization purposes. Otherwise
  1233. * use the export() method to export bean data to arrays.
  1234. * This method returns an array with the properties array and
  1235. * the type (string).
  1236. *
  1237. * @return array
  1238. */
  1239. public function getPropertiesAndType()
  1240. {
  1241. return array( $this->properties, $this->__info['type'] );
  1242. }
  1243. /**
  1244. * Turns a camelcase property name into an underscored property name.
  1245. * Examples:
  1246. * oneACLRoute -> one_acl_route
  1247. * camelCase -> camel_case
  1248. *
  1249. * Also caches the result to improve performance.
  1250. *
  1251. * @param string $property
  1252. *
  1253. * @return string
  1254. */
  1255. public function beau( $property )
  1256. {
  1257. static $beautifulColumns = array();
  1258. if ( ctype_lower( $property ) ) return $property;
  1259. if (
  1260. strpos( $property, 'own' ) === 0
  1261. || strpos( $property, 'xown' ) === 0
  1262. || strpos( $property, 'shared' ) === 0
  1263. ) {
  1264. $property = preg_replace( '/List$/', '', $property );
  1265. return $property;
  1266. }
  1267. if ( !isset( $beautifulColumns[$property] ) ) {
  1268. $beautifulColumns[$property] = AQueryWriter::camelsSnake( $property );
  1269. }
  1270. return $beautifulColumns[$property];
  1271. }
  1272. /**
  1273. * Returns current status of modification flags.
  1274. *
  1275. * @return string
  1276. */
  1277. public function getModFlags()
  1278. {
  1279. $modFlags = '';
  1280. if ($this->aliasName !== NULL) $modFlags .= 'a';
  1281. if ($this->fetchType !== NULL) $modFlags .= 'f';
  1282. if ($this->noLoad === TRUE) $modFlags .= 'n';
  1283. if ($this->all === TRUE) $modFlags .= 'r';
  1284. if ($this->withSql !== '') $modFlags .= 'w';
  1285. return $modFlags;
  1286. }
  1287. /**
  1288. * Clears all modifiers.
  1289. *
  1290. * @return self
  1291. */
  1292. public function clearModifiers()
  1293. {
  1294. $this->withSql = '';
  1295. $this->withParams = array();
  1296. $this->aliasName = NULL;
  1297. $this->fetchType = NULL;
  1298. $this->noLoad = FALSE;
  1299. $this->all = FALSE;
  1300. $this->via = NULL;
  1301. return $this;
  1302. }
  1303. /**
  1304. * Determines whether a list is opened in exclusive mode or not.
  1305. * If a list has been opened in exclusive mode this method will return TRUE,
  1306. * othwerwise it will return FALSE.
  1307. *
  1308. * @param string $listName name of the list to check
  1309. *
  1310. * @return boolean
  1311. */
  1312. public function isListInExclusiveMode( $listName )
  1313. {
  1314. $listName = $this->beau( $listName );
  1315. if ( strpos( $listName, 'xown' ) === 0 && ctype_upper( substr( $listName, 4, 1 ) ) ) {
  1316. $listName = substr($listName, 1);
  1317. }
  1318. $listName = lcfirst( substr( $listName, 3 ) );
  1319. return ( isset( $this->__info['sys.exclusive-'.$listName] ) && $this->__info['sys.exclusive-'.$listName] );
  1320. }
  1321. /**
  1322. * Magic Getter. Gets the value for a specific property in the bean.
  1323. * If the property does not exist this getter will make sure no error
  1324. * occurs. This is because RedBean allows you to query (probe) for
  1325. * properties. If the property can not be found this method will
  1326. * return NULL instead.
  1327. *
  1328. * @param string $property name of the property you wish to obtain the value of
  1329. *
  1330. * @return mixed
  1331. */
  1332. public function &__get( $property )
  1333. {
  1334. $isEx = FALSE;
  1335. $isOwn = FALSE;
  1336. $isShared = FALSE;
  1337. if ( !ctype_lower( $property ) ) {
  1338. $property = $this->beau( $property );
  1339. if ( strpos( $property, 'xown' ) === 0 && ctype_upper( substr( $property, 4, 1 ) ) ) {
  1340. $property = substr($property, 1);
  1341. $listName = lcfirst( substr( $property, 3 ) );
  1342. $isEx = TRUE;
  1343. $isOwn = TRUE;
  1344. $this->__info['sys.exclusive-'.$listName] = TRUE;
  1345. } elseif ( strpos( $property, 'own' ) === 0 && ctype_upper( substr( $property, 3, 1 ) ) ) {
  1346. $isOwn = TRUE;
  1347. $listName = lcfirst( substr( $property, 3 ) );
  1348. } elseif ( strpos( $property, 'shared' ) === 0 && ctype_upper( substr( $property, 6, 1 ) ) ) {
  1349. $isShared = TRUE;
  1350. }
  1351. }
  1352. $fieldLink = $property . '_id';
  1353. $exists = isset( $this->properties[$property] );
  1354. //If not exists and no field link and no list, bail out.
  1355. if ( !$exists && !isset($this->$fieldLink) && (!$isOwn && !$isShared )) {
  1356. $this->withSql = '';
  1357. $this->withParams = array();
  1358. $this->aliasName = NULL;
  1359. $this->fetchType = NULL;
  1360. $this->noLoad = FALSE;
  1361. $this->all = FALSE;
  1362. $this->via = NULL;
  1363. $NULL = NULL;
  1364. return $NULL;
  1365. }
  1366. $hasAlias = (!is_null($this->aliasName));
  1367. $differentAlias = ($hasAlias && $isOwn && isset($this->__info['sys.alias.'.$listName])) ?
  1368. ($this->__info['sys.alias.'.$listName] !== $this->aliasName) : FALSE;
  1369. $hasSQL = ($this->withSql !== '' || $this->via !== NULL);
  1370. $hasAll = (boolean) ($this->all);
  1371. //If exists and no list or exits and list not changed, bail out.
  1372. if ( $exists && ((!$isOwn && !$isShared ) || (!$hasSQL && !$differentAlias && !$hasAll)) ) {
  1373. $this->withSql = '';
  1374. $this->withParams = array();
  1375. $this->aliasName = NULL;
  1376. $this->fetchType = NULL;
  1377. $this->noLoad = FALSE;
  1378. $this->all = FALSE;
  1379. $this->via = NULL;
  1380. return $this->properties[$property];
  1381. }
  1382. list( $redbean, , , $toolbox ) = $this->beanHelper->getExtractedToolbox();
  1383. if ( isset( $this->$fieldLink ) ) {
  1384. $this->__info['tainted'] = TRUE;
  1385. if ( isset( $this->__info["sys.parentcache.$property"] ) ) {
  1386. $bean = $this->__info["sys.parentcache.$property"];
  1387. } else {
  1388. $type = $this->getAlias( $property );
  1389. $bean = $redbean->load( $type, $this->properties[$fieldLink] );
  1390. }
  1391. $this->properties[$property] = $bean;
  1392. $this->withSql = '';
  1393. $this->withParams = array();
  1394. $this->aliasName = NULL;
  1395. $this->fetchType = NULL;
  1396. $this->noLoad = FALSE;
  1397. $this->all = FALSE;
  1398. $this->via = NULL;
  1399. return $this->properties[$property];
  1400. }
  1401. //Implicit: elseif ( $isOwn || $isShared ) {
  1402. if ( $this->noLoad ) {
  1403. $beans = array();
  1404. } elseif ( $isOwn ) {
  1405. $beans = $this->getOwnList( $listName, $redbean );
  1406. } else {
  1407. $beans = $this->getSharedList( lcfirst( substr( $property, 6 ) ), $redbean, $toolbox );
  1408. }
  1409. $this->properties[$property] = $beans;
  1410. $this->__info["sys.shadow.$property"] = $beans;
  1411. $this->__info['tainted'] = TRUE;
  1412. $this->withSql = '';
  1413. $this->withParams = array();
  1414. $this->aliasName = NULL;
  1415. $this->fetchType = NULL;
  1416. $this->noLoad = FALSE;
  1417. $this->all = FALSE;
  1418. $this->via = NULL;
  1419. return $this->properties[$property];
  1420. }
  1421. /**
  1422. * Magic Setter. Sets the value for a specific property.
  1423. * This setter acts as a hook for OODB to mark beans as tainted.
  1424. * The tainted meta property can be retrieved using getMeta("tainted").
  1425. * The tainted meta property indicates whether a bean has been modified and
  1426. * can be used in various caching mechanisms.
  1427. *
  1428. * @param string $property name of the property you wish to assign a value to
  1429. * @param mixed $value the value you want to assign
  1430. *
  1431. * @return void
  1432. *
  1433. * @throws Security
  1434. */
  1435. public function __set( $property, $value )
  1436. {
  1437. $isEx = FALSE;
  1438. $isOwn = FALSE;
  1439. $isShared = FALSE;
  1440. if ( !ctype_lower( $property ) ) {
  1441. $property = $this->beau( $property );
  1442. if ( strpos( $property, 'xown' ) === 0 && ctype_upper( substr( $property, 4, 1 ) ) ) {
  1443. $property = substr($property, 1);
  1444. $listName = lcfirst( substr( $property, 3 ) );
  1445. $isEx = TRUE;
  1446. $isOwn = TRUE;
  1447. $this->__info['sys.exclusive-'.$listName] = TRUE;
  1448. } elseif ( strpos( $property, 'own' ) === 0 && ctype_upper( substr( $property, 3, 1 ) ) ) {
  1449. $isOwn = TRUE;
  1450. $listName = lcfirst( substr( $property, 3 ) );
  1451. } elseif ( strpos( $property, 'shared' ) === 0 && ctype_upper( substr( $property, 6, 1 ) ) ) {
  1452. $isShared = TRUE;
  1453. }
  1454. }
  1455. $hasAlias = (!is_null($this->aliasName));
  1456. $differentAlias = ($hasAlias && $isOwn && isset($this->__info['sys.alias.'.$listName])) ?
  1457. ($this->__info['sys.alias.'.$listName] !== $this->aliasName) : FALSE;
  1458. $hasSQL = ($this->withSql !== '' || $this->via !== NULL);
  1459. $exists = isset( $this->properties[$property] );
  1460. $fieldLink = $property . '_id';
  1461. if ( ($isOwn || $isShared) && (!$exists || $hasSQL || $differentAlias) ) {
  1462. if ( !$this->noLoad ) {
  1463. list( $redbean, , , $toolbox ) = $this->beanHelper->getExtractedToolbox();
  1464. if ( $isOwn ) {
  1465. $beans = $this->getOwnList( $listName, $redbean );
  1466. } else {
  1467. $beans = $this->getSharedList( lcfirst( substr( $property, 6 ) ), $redbean, $toolbox );
  1468. }
  1469. $this->__info["sys.shadow.$property"] = $beans;
  1470. }
  1471. }
  1472. $this->withSql = '';
  1473. $this->withParams = array();
  1474. $this->aliasName = NULL;
  1475. $this->fetchType = NULL;
  1476. $this->noLoad = FALSE;
  1477. $this->all = FALSE;
  1478. $this->via = NULL;
  1479. $this->__info['tainted'] = TRUE;
  1480. if ( array_key_exists( $fieldLink, $this->properties ) && !( $value instanceof OODBBean ) ) {
  1481. if ( is_null( $value ) || $value === FALSE ) {
  1482. unset( $this->properties[ $property ]);
  1483. $this->properties[ $fieldLink ] = NULL;
  1484. return;
  1485. } else {
  1486. throw new RedException( 'Cannot cast to bean.' );
  1487. }
  1488. }
  1489. if ( $value === FALSE ) {
  1490. $value = '0';
  1491. } elseif ( $value === TRUE ) {
  1492. $value = '1';
  1493. } elseif ( $value instanceof \DateTime ) {
  1494. $value = $value->format( 'Y-m-d H:i:s' );
  1495. }
  1496. $this->properties[$property] = $value;
  1497. }
  1498. /**
  1499. * Sets a property directly, for internal use only.
  1500. *
  1501. * @param string $property property
  1502. * @param mixed $value value
  1503. * @param boolean $updateShadow whether you want to update the shadow
  1504. * @param boolean $taint whether you want to mark the bean as tainted
  1505. *
  1506. * @return void
  1507. */
  1508. public function setProperty( $property, $value, $updateShadow = FALSE, $taint = FALSE )
  1509. {
  1510. $this->properties[$property] = $value;
  1511. if ( $updateShadow ) {
  1512. $this->__info['sys.shadow.' . $property] = $value;
  1513. }
  1514. if ( $taint ) {
  1515. $this->__info['tainted'] = TRUE;
  1516. }
  1517. }
  1518. /**
  1519. * Returns the value of a meta property. A meta property
  1520. * contains extra information about the bean object that will not
  1521. * get stored in the database. Meta information is used to instruct
  1522. * RedBean as well as other systems how to deal with the bean.
  1523. * For instance: $bean->setMeta("buildcommand.unique", array(
  1524. * array("column1", "column2", "column3") ) );
  1525. * Will add a UNIQUE constraint for the bean on columns: column1, column2 and
  1526. * column 3.
  1527. * To access a Meta property we use a dot separated notation.
  1528. * If the property cannot be found this getter will return NULL instead.
  1529. *
  1530. * @param string $path path
  1531. * @param mixed $default default value
  1532. *
  1533. * @return mixed
  1534. */
  1535. public function getMeta( $path, $default = NULL )
  1536. {
  1537. return ( isset( $this->__info[$path] ) ) ? $this->__info[$path] : $default;
  1538. }
  1539. /**
  1540. * Stores a value in the specified Meta information property. $value contains
  1541. * the value you want to store in the Meta section of the bean and $path
  1542. * specifies the dot separated path to the property. For instance "my.meta.property".
  1543. * If "my" and "meta" do not exist they will be created automatically.
  1544. *
  1545. * @param string $path path
  1546. * @param mixed $value value
  1547. *
  1548. * @return OODBBean
  1549. */
  1550. public function setMeta( $path, $value )
  1551. {
  1552. $this->__info[$path] = $value;
  1553. return $this;
  1554. }
  1555. /**
  1556. * Copies the meta information of the specified bean
  1557. * This is a convenience method to enable you to
  1558. * exchange meta information easily.
  1559. *
  1560. * @param OODBBean $bean
  1561. *
  1562. * @return OODBBean
  1563. */
  1564. public function copyMetaFrom( OODBBean $bean )
  1565. {
  1566. $this->__info = $bean->__info;
  1567. return $this;
  1568. }
  1569. /**
  1570. * Sends the call to the registered model.
  1571. *
  1572. * @param string $method name of the method
  1573. * @param array $args argument list
  1574. *
  1575. * @return mixed
  1576. */
  1577. public function __call( $method, $args )
  1578. {
  1579. if ( !isset( $this->__info['model'] ) ) {
  1580. $model = $this->beanHelper->getModelForBean( $this );
  1581. if ( !$model ) {
  1582. return NULL;
  1583. }
  1584. $this->__info['model'] = $model;
  1585. }
  1586. if ( !method_exists( $this->__info['model'], $method ) ) {
  1587. return NULL;
  1588. }
  1589. return call_user_func_array( array( $this->__info['model'], $method ), $args );
  1590. }
  1591. /**
  1592. * Implementation of __toString Method
  1593. * Routes call to Model. If the model implements a __toString() method this
  1594. * method will be called and the result will be returned. In case of an
  1595. * echo-statement this result will be printed. If the model does not
  1596. * implement a __toString method, this method will return a JSON
  1597. * representation of the current bean.
  1598. *
  1599. * @return string
  1600. */
  1601. public function __toString()
  1602. {
  1603. $string = $this->__call( '__toString', array() );
  1604. if ( $string === NULL ) {
  1605. return json_encode( $this->properties );
  1606. } else {
  1607. return $string;
  1608. }
  1609. }
  1610. /**
  1611. * Implementation of Array Access Interface, you can access bean objects
  1612. * like an array.
  1613. * Call gets routed to __set.
  1614. *
  1615. * @param mixed $offset offset string
  1616. * @param mixed $value value
  1617. *
  1618. * @return void
  1619. */
  1620. public function offsetSet( $offset, $value )
  1621. {
  1622. $this->__set( $offset, $value );
  1623. }
  1624. /**
  1625. * Implementation of Array Access Interface, you can access bean objects
  1626. * like an array.
  1627. *
  1628. * Array functions do not reveal x-own-lists and list-alias because
  1629. * you dont want duplicate entries in foreach-loops.
  1630. * Also offers a slight performance improvement for array access.
  1631. *
  1632. * @param mixed $offset property
  1633. *
  1634. * @return boolean
  1635. */
  1636. public function offsetExists( $offset )
  1637. {
  1638. return $this->__isset( $offset );
  1639. }
  1640. /**
  1641. * Implementation of Array Access Interface, you can access bean objects
  1642. * like an array.
  1643. * Unsets a value from the array/bean.
  1644. *
  1645. * Array functions do not reveal x-own-lists and list-alias because
  1646. * you dont want duplicate entries in foreach-loops.
  1647. * Also offers a slight performance improvement for array access.
  1648. *
  1649. * @param mixed $offset property
  1650. *
  1651. * @return void
  1652. */
  1653. public function offsetUnset( $offset )
  1654. {
  1655. $this->__unset( $offset );
  1656. }
  1657. /**
  1658. * Implementation of Array Access Interface, you can access bean objects
  1659. * like an array.
  1660. * Returns value of a property.
  1661. *
  1662. * Array functions do not reveal x-own-lists and list-alias because
  1663. * you dont want duplicate entries in foreach-loops.
  1664. * Also offers a slight performance improvement for array access.
  1665. *
  1666. * @param mixed $offset property
  1667. *
  1668. * @return mixed
  1669. */
  1670. public function &offsetGet( $offset )
  1671. {
  1672. return $this->__get( $offset );
  1673. }
  1674. /**
  1675. * Chainable method to cast a certain ID to a bean; for instance:
  1676. * $person = $club->fetchAs('person')->member;
  1677. * This will load a bean of type person using member_id as ID.
  1678. *
  1679. * @param string $type preferred fetch type
  1680. *
  1681. * @return OODBBean
  1682. */
  1683. public function fetchAs( $type )
  1684. {
  1685. $this->fetchType = $type;
  1686. return $this;
  1687. }
  1688. /**
  1689. * For polymorphic bean relations.
  1690. * Same as fetchAs but uses a column instead of a direct value.
  1691. *
  1692. * @param string $column
  1693. *
  1694. * @return OODBBean
  1695. */
  1696. public function poly( $field )
  1697. {
  1698. return $this->fetchAs( $this->$field );
  1699. }
  1700. /**
  1701. * Traverses a bean property with the specified function.
  1702. * Recursively iterates through the property invoking the
  1703. * function for each bean along the way passing the bean to it.
  1704. *
  1705. * Can be used together with with, withCondition, alias and fetchAs.
  1706. *
  1707. * @param string $property property
  1708. * @param closure $function function
  1709. *
  1710. * @return OODBBean
  1711. */
  1712. public function traverse( $property, $function, $maxDepth = NULL )
  1713. {
  1714. $this->via = NULL;
  1715. if ( strpos( $property, 'shared' ) !== FALSE ) {
  1716. throw new RedException( 'Traverse only works with (x)own-lists.' );
  1717. }
  1718. if ( !is_null( $maxDepth ) ) {
  1719. if ( !$maxDepth-- ) return $this;
  1720. }
  1721. $oldFetchType = $this->fetchType;
  1722. $oldAliasName = $this->aliasName;
  1723. $oldWith = $this->withSql;
  1724. $oldBindings = $this->withParams;
  1725. $beans = $this-

Large files files are truncated, but you can click here to view the full file