+ * @date 2013.07.03
+ */
+final class NoSQLExpression implements LogicalObject, MappableObject {
+
+ const C_TYPE = 1;
+ const C_FIELD = 2;
+ const C_VALUE = 3;
+
+ const V_LEFT = 101;
+ const V_RIGHT = 102;
+
+ const EXP_EQ = 1001;
+ const EXP_NOT_EQ = 1002;
+ const EXP_GT = 1003;
+ const EXP_GTE = 1004;
+ const EXP_LT = 1005;
+ const EXP_LTE = 1006;
+ const EXP_BTW_STR = 1007;
+ const EXP_BTW_SFT = 1008;
+ const EXP_IN = 1009;
+ const EXP_NOT_IN = 1010;
+ const EXP_TYPE = 1100;
+
+ /**
+ * true = объединять условия по И
+ * false = разделять условия по ИЛИ
+ * @var bool
+ */
+ protected $unite = null;
+
+ protected $fields = array();
+ protected $conditions = array();
+
+// public static function create($unite = true) {
+// return new self($unite);
+// }
+
+ /**
+ * Создает условие типа И
+ * @static
+ * @return NoSQLExpression
+ */
+ public static function createAnd() {
+ return new self(true);
+ }
+
+ /**
+ * Создает условие типа ИЛИ
+ * @static
+ * @return NoSQLExpression
+ */
+ public static function createOr() {
+ return new self(false);
+ }
+
+ public function __construct($unite = true) {
+ $this->unite = (bool)$unite;
+ }
+
+ /**
+ * Замена Assert::checkInteger
+ * @param $variable
+ * @return bool
+ */
+ public static function checkComparable($variable) {
+ if (Assert::checkScalar($variable) || $variable instanceof MongoDate) {
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Замена Assert::IsInteger
+ * @param $variable
+ * @param $message
+ * @throws WrongArgumentException
+ */
+ protected static function assertIsComparable($variable, $message = null) {
+ if (!self::checkComparable($variable)) {
+ throw new WrongArgumentException(
+ $message.', '.Assert::dumpArgument($variable)
+ );
+ }
+ }
+/// field list
+//@{
+ /**
+ * @param string $fieldName
+ * @return NoSQLExpression
+ */
+ public function addField($fieldName) {
+ $this->fields = $fieldName;
+ return $this;
+ }
+//@}
+
+/// condition setters
+//@{
+ public function addEq($field, $value) {
+ if (is_string($value) && Assert::checkInteger($value)) {
+ $dbValue = array($value, (int)$value);
+ } elseif (is_string($value) && Assert::checkFloat($value)) {
+ $dbValue = array($value, (float)$value);
+ } else {
+ $dbValue = $value;
+ }
+
+ if (is_array($dbValue) /* weak typing */) {
+ $this->conditions[] = array(
+ self::C_TYPE => self::EXP_IN,
+ self::C_FIELD => (string)$field,
+ self::C_VALUE => $dbValue,
+ );
+ } else {
+ $this->conditions[] = array(
+ self::C_TYPE => self::EXP_EQ,
+ self::C_FIELD => (string)$field,
+ self::C_VALUE => $dbValue,
+ );
+ }
+ return $this;
+ }
+
+ public function addNotEq($field, $value) {
+ $this->conditions[] = array(
+ self::C_TYPE => self::EXP_NOT_EQ,
+ self::C_FIELD => (string)$field,
+ self::C_VALUE => Assert::checkInteger($value) ? (int)$value : $value,
+ );
+ return $this;
+ }
+
+ public function addGt($field, $value) {
+ self::assertIsComparable($value);
+ $this->conditions[] = array(
+ self::C_TYPE => self::EXP_GT,
+ self::C_FIELD => (string)$field,
+ self::C_VALUE => $value,
+ );
+ return $this;
+ }
+
+ public function addGte($field, $value) {
+ self::assertIsComparable($value);
+ $this->conditions[] = array(
+ self::C_TYPE => self::EXP_GTE,
+ self::C_FIELD => (string)$field,
+ self::C_VALUE => $value,
+ );
+ return $this;
+ }
+
+ public function addLt($field, $value) {
+ self::assertIsComparable($value);
+ $this->conditions[] = array(
+ self::C_TYPE => self::EXP_LT,
+ self::C_FIELD => (string)$field,
+ self::C_VALUE => $value,
+ );
+ return $this;
+ }
+
+ public function addLte($field, $value) {
+ self::assertIsComparable($value);
+ $this->conditions[] = array(
+ self::C_TYPE => self::EXP_LTE,
+ self::C_FIELD => (string)$field,
+ self::C_VALUE => $value,
+ );
+ return $this;
+ }
+
+ public function addBetweenStrict($field, $left, $right) {
+ self::assertIsComparable($left);
+ self::assertIsComparable($right);
+ $this->conditions[] = array(
+ self::C_TYPE => self::EXP_BTW_STR,
+ self::C_FIELD => (string)$field,
+ self::C_VALUE => array( self::V_LEFT=>$left, self::V_RIGHT=>$right ),
+ );
+ return $this;
+ }
+
+ public function addBetweenSoft($field, $left, $right) {
+ self::assertIsComparable($left);
+ self::assertIsComparable($right);
+ $this->conditions[] = array(
+ self::C_TYPE => self::EXP_BTW_SFT,
+ self::C_FIELD => (string)$field,
+ self::C_VALUE => array( self::V_LEFT=>$left, self::V_RIGHT=>$right ),
+ );
+ return $this;
+ }
+
+ public function addIn($field, array $value) {
+ /*foreach($value as &$inVal) {
+ if( is_null($inVal) ) {
+ $inVal = null;
+ } elseif( Assert::checkInteger($inVal) ) {
+ $inVal = (int)$inVal;
+ } else {
+ $inVal = (string)$inVal;
+ }
+ }*/
+ $this->conditions[] = array(
+ self::C_TYPE => self::EXP_IN,
+ self::C_FIELD => (string)$field,
+ self::C_VALUE => $value,
+ );
+ return $this;
+ }
+
+ public function addNotIn($field, $value) {
+ foreach($value as &$inVal) {
+ if(Assert::checkInteger($inVal)) {
+ $inVal = (int)$inVal;
+ } else {
+ $inVal = (string)$inVal;
+ }
+ }
+ $this->conditions[] = array(
+ self::C_TYPE => self::EXP_NOT_IN,
+ self::C_FIELD => (string)$field,
+ self::C_VALUE => $value,
+ );
+ return $this;
+ }
+
+ public function addType($field, $value) {
+ $this->conditions[] = array(
+ self::C_TYPE => self::EXP_TYPE,
+ self::C_FIELD => (string)$field,
+ self::C_VALUE => (int)$value,
+ );
+ return $this;
+ }
+
+//@}
+
+/// helper functions
+//@{
+//@}
+
+/// condition setters
+//@{
+ public function getFieldList() {
+ return $this->fields;
+ }
+
+ /**
+ * @return array
+ */
+ public function getConditions() {
+ return $this->conditions;
+ }
+
+ public function toMongoQuery() {
+ if( empty($this->conditions) ) {
+ //throw new WrongStateException('Sorry, query conditions are empty!');
+ return array();
+ }
+ // make query
+ $query = array();
+ foreach($this->conditions as $condition) {
+ switch($condition[self::C_TYPE]) {
+ case self::EXP_EQ: {
+ if( $this->unite ) {
+ $query[ $condition[self::C_FIELD] ] = $condition[self::C_VALUE];
+ } else {
+ $query[] = array( $condition[self::C_FIELD] => $condition[self::C_VALUE] );
+ }
+
+ } break;
+ case self::EXP_NOT_EQ: {
+ if( $this->unite ) {
+ $query[ $condition[self::C_FIELD] ] = array('$ne' => $condition[self::C_VALUE]);
+ } else {
+ $query[] = array( $condition[self::C_FIELD] => array('$ne' => $condition[self::C_VALUE]) );
+ }
+ } break;
+ case self::EXP_GT: {
+ if( $this->unite ) {
+ $query[ $condition[self::C_FIELD] ] = array('$gt' => $condition[self::C_VALUE]);
+ } else {
+ $query[] = array( $condition[self::C_FIELD] => array('$gt' => $condition[self::C_VALUE]) );
+ }
+ } break;
+ case self::EXP_GTE: {
+ if( $this->unite ) {
+ $query[ $condition[self::C_FIELD] ] = array('$gte' => $condition[self::C_VALUE]);
+ } else {
+ $query[] = array( $condition[self::C_FIELD] => array('$gte' => $condition[self::C_VALUE]) );
+ }
+ } break;
+ case self::EXP_LT: {
+ if( $this->unite ) {
+ $query[ $condition[self::C_FIELD] ] = array('$lt' => $condition[self::C_VALUE]);
+ } else {
+ $query[] = array( $condition[self::C_FIELD] => array('$lt' => $condition[self::C_VALUE]) );
+ }
+ } break;
+ case self::EXP_LTE: {
+ if( $this->unite ) {
+ $query[ $condition[self::C_FIELD] ] = array('$lte' => $condition[self::C_VALUE]);
+ } else {
+ $query[] = array( $condition[self::C_FIELD] => array('$lte' => $condition[self::C_VALUE]) );
+ }
+ } break;
+ case self::EXP_BTW_STR: {
+ if( $this->unite ) {
+ $query[ $condition[self::C_FIELD] ] = array('$gt' => $condition[self::C_VALUE][self::V_LEFT], '$lt' => $condition[self::C_VALUE][self::V_RIGHT]);
+ } else {
+ $query[] = array( $condition[self::C_FIELD] => array('$gt' => $condition[self::C_VALUE][self::V_LEFT], '$lt' => $condition[self::C_VALUE][self::V_RIGHT]) );
+ }
+ } break;
+ case self::EXP_BTW_SFT: {
+ if( $this->unite ) {
+ $query[ $condition[self::C_FIELD] ] = array('$gte' => $condition[self::C_VALUE][self::V_LEFT], '$lte' => $condition[self::C_VALUE][self::V_RIGHT]);
+ } else {
+ $query[] = array( $condition[self::C_FIELD] => array('$gte' => $condition[self::C_VALUE][self::V_LEFT], '$lte' => $condition[self::C_VALUE][self::V_RIGHT]) );
+ }
+ } break;
+ case self::EXP_IN: {
+ if( $this->unite ) {
+ $query[ $condition[self::C_FIELD] ] = array('$in' => $condition[self::C_VALUE]);
+ } else {
+ $query[] = array( $condition[self::C_FIELD] => array('$in' => $condition[self::C_VALUE]) );
+ }
+ } break;
+ case self::EXP_NOT_IN: {
+ if( $this->unite ) {
+ $query[ $condition[self::C_FIELD] ] = array('$nin' => $condition[self::C_VALUE]);
+ } else {
+ $query[] = array( $condition[self::C_FIELD] => array('$nin' => $condition[self::C_VALUE]) );
+ }
+ } break;
+ case self::EXP_TYPE: {
+ if( $this->unite ) {
+ $query[ $condition[self::C_FIELD] ] = array('$type' => $condition[self::C_VALUE]);
+ } else {
+ $query[] = array( $condition[self::C_FIELD] => array('$type' => $condition[self::C_VALUE]) );
+ }
+ } break;
+ default: {
+ throw new WrongStateException( 'Sorry, I do not know how to work with you condition!' );
+ } break;
+ }
+ }
+ if( !$this->unite ) {
+ $query = array('$or' => $query);
+ }
+ return $query;
+ }
+//@}
+
+/// parent object function implenetation
+//@{
+ public function toDialectString(Dialect $dialect) {
+ throw new UnsupportedMethodException('NoSQLExpression does not support method "toDialectStringg"');
+ }
+
+ public function toBoolean(Form $form) {
+ throw new UnsupportedMethodException('NoSQLExpression does not support method "toBoolean"');
+ }
+
+ public function toMapped(ProtoDAO $dao, JoinCapableQuery $query) {
+ throw new UnsupportedMethodException('NoSQLExpression does not support method "toMapped"');
+ }
+//@}
+
+}
diff --git a/core/Logic/PostfixUnaryExpression.class.php b/core/Logic/PostfixUnaryExpression.class.php
old mode 100644
new mode 100755
diff --git a/core/Logic/PrefixUnaryExpression.class.php b/core/Logic/PrefixUnaryExpression.class.php
old mode 100644
new mode 100755
diff --git a/core/Logic/SQLFunctionExpression.class.php b/core/Logic/SQLFunctionExpression.class.php
new file mode 100644
index 0000000000..f3f560fe5d
--- /dev/null
+++ b/core/Logic/SQLFunctionExpression.class.php
@@ -0,0 +1,54 @@
+arguments = $arguments;
+ }
+
+ public function toDialectString(Dialect $dialect)
+ {
+ /** @var SQLFunction $sql */
+ $sql = call_user_func_array('SQLFunction::create', $this->arguments);
+ return $sql->toDialectString( $dialect );
+ }
+
+ public function toBoolean(Form $form)
+ {
+ throw new UnsupportedMethodException();
+ }
+
+ }
\ No newline at end of file
diff --git a/core/NoSQL/MongoBase.class.php b/core/NoSQL/MongoBase.class.php
new file mode 100755
index 0000000000..b6de8de07c
--- /dev/null
+++ b/core/NoSQL/MongoBase.class.php
@@ -0,0 +1,767 @@
+
+ * @date 2012.03.27
+ */
+class MongoBase extends NoSQL {
+
+ const C_TABLE = 1001;
+ const C_FIELDS = 1002;
+ const C_QUERY = 1003;
+ const C_ORDER = 1004;
+ const C_LIMIT = 1005;
+ const C_SKIP = 1006;
+
+ /**
+ * @var string|null
+ */
+ protected $connectionString = null;
+
+ /**
+ * @var array|null
+ */
+ protected $connectionOptions = null;
+
+ /**
+ * @var Mongo
+ */
+ protected $link = null;
+
+ /**
+ * @var MongoDB
+ */
+ protected $db = null;
+
+ /**
+ * @var int параметр "safe" ("w" в 1.3.0+)
+ */
+ protected $writeConcern = 1;
+
+ /** @var bool */
+ protected $isRetrying = false;
+
+ protected function reconnectAndRetry($function, $args) {
+ // have you tried turning it off and on again? (c)
+ $this->disconnect();
+ sleep(1);
+ $this->connect();
+ $this->isRetrying = true;
+ try {
+ call_user_func_array(array($this, $function), $args);
+ } catch (Exception $e) {
+ $this->isRetrying = false;
+ throw $e;
+ }
+ $this->isRetrying = false;
+ }
+
+ /**
+ * @return MongoBase
+ * @throws NoSQLException
+ */
+ public function connect() {
+ // в зависимости от версии драйвера создаем нужного клиента
+ $Mongo = self::getClientClass();
+
+ if (empty($this->connectionString)) {
+ $conn =
+ 'mongodb://'
+ .($this->username && $this->password ? "{$this->username}:{$this->password}@" : null)
+ .$this->hostname
+ .($this->port ? ":{$this->port}" : null);
+ } else {
+ preg_match('#(.+)/(\w+)#', $this->connectionString, $matches);
+ $conn = $matches[1];
+ $base = $matches[2];
+ $this->setBasename($base);
+ }
+
+ $options = array('connect' => true, 'slaveOkay' => false);
+ if (!empty($this->connectionOptions)) {
+ $options = array_merge($options, $this->connectionOptions);
+ }
+
+ if ($this->persistent) {
+ $options['persist'] = $this->hostname.'-'.$this->basename;
+ }
+ $readPreference = isset($options['slaveOkay']) && $options['slaveOkay'];
+ if( $Mongo==='MongoClient' ) {
+ $options['readPreference'] = $readPreference ? $Mongo::RP_SECONDARY_PREFERRED : $Mongo::RP_PRIMARY_PREFERRED;
+ unset($options['slaveOkay']);
+ }
+ try {
+ $this->link = new $Mongo($conn, $options);
+ $this->db = $this->link->selectDB($this->basename);
+ if( method_exists($Mongo, 'setReadPreference') ) {
+ $this->link->setReadPreference($readPreference ? $Mongo::RP_SECONDARY_PREFERRED : $Mongo::RP_PRIMARY_PREFERRED);
+ } else {
+ $this->link->setSlaveOkay($options['slaveOkay']);
+ }
+ if (isset($options['w'])) {
+ $this->writeConcern = $options['w'];
+ }
+
+ } catch(MongoConnectionException $e) {
+ throw new NoSQLException(
+ 'can not connect to MongoBase server: '.$e->getMessage()
+ );
+ } catch(InvalidArgumentException $e) {
+ throw new NoSQLException(
+ 'can not select DB in MongoBase: '.$e->getMessage()
+ );
+ }
+
+ return $this;
+ }
+
+ public function switchToPrimary() {
+ $this->connectionOptions['slaveOkay'] = false;
+ $this->connectionOptions['readPreference'] = 'primary';
+ $this->connect();
+ }
+
+ /**
+ * @return MongoBase
+ */
+ public function disconnect() {
+ if( $this->isConnected() ) {
+ $this->link->close();
+ }
+ $this->link = null;
+ $this->db = null;
+
+ return $this;
+ }
+
+ /**
+ * @return bool
+ */
+ public function isConnected() {
+ return ($this->link instanceof Mongo && $this->link->connected);
+ }
+
+ /**
+ * @param $connectionString
+ * @return MongoBase
+ */
+ public function setConnectionString($connectionString) {
+ $this->connectionString = $connectionString;
+ return $this;
+ }
+
+ /**
+ * @param $connectionOptions
+ * @return MongoBase
+ */
+ public function setConnectionOptions($connectionOptions) {
+ $this->connectionOptions = $connectionOptions;
+ return $this;
+ }
+
+ /**
+ * @param string $sequence
+ * @return MongoId
+ */
+ public function obtainSequence($sequence) {
+ return new MongoId(mb_strtolower(trim($sequence)));
+ }
+
+ public function selectOne($table, $key) {
+ $row =
+ $this
+ ->db
+ ->selectCollection($table)
+ ->findOne( array('_id' => new MongoId($key)) );
+ if( is_null($row) ) {
+ throw new ObjectNotFoundException( 'Object with id "'.$key.'" in table "'.$table.'" not found!' );
+ }
+ // return clean row
+ return $this->decodeId($row);
+ }
+
+ public function selectList($table, array $keys) {
+ // quering
+ $cursor =
+ $this
+ ->db
+ ->selectCollection($table)
+ ->find( array('_id' => array('$in'=>$this->makeIdList($keys)) ) );
+ // recieving objects
+ $rows = array();
+ foreach ($cursor as $row) {
+ $rows[] = $this->decodeId($row);
+ }
+ // return result
+ return $rows;
+ }
+
+ public function insert($table, array $row, $options = array()) {
+ $row = $this->encodeId($row);
+ $options = array_merge(
+ array('safe' => true),
+ $options
+ );
+ if ($options['safe']) {
+ if ($this->checkVersion('1.3.0')) {
+ $options['w'] = $this->writeConcern;
+ unset($options['safe']);
+ } else {
+ $options['safe'] = $this->writeConcern;
+ }
+ }
+
+ $isSafe = isset($options['safe']) || isset($options['w']);
+
+ try {
+ $result =
+ $this->db
+ ->selectCollection($table)
+ ->insert($row, $options);
+
+ if ($isSafe && is_array($result)) {
+ $this->checkResult($result);
+ }
+
+ } catch (Exception $e) {
+ if ($this->isRetrying) {
+ if ($e instanceof MongoCursorException && $e->getCode() == 11000) {
+ // E11000 == duplicate key error index
+ // если это вылезло при повторной попытке, значит первый раз таки вставили
+ } else {
+ throw $e;
+ }
+ } elseif ($e instanceof MongoCursorTimeoutException) {
+ $this->reconnectAndRetry(__FUNCTION__, func_get_args());
+ } else {
+ throw $e;
+ }
+ }
+
+ // return clean row
+ return $this->decodeId($row);
+ }
+
+ public function batchInsert($table, array $rows, array $options = array()) {
+ $options = array_merge(
+ array('safe' => true),
+ $options
+ );
+ if ($options['safe']) {
+ if ($this->checkVersion('1.3.0')) {
+ $options['w'] = $this->writeConcern;
+ unset($options['safe']);
+ } else {
+ $options['safe'] = $this->writeConcern;
+ }
+ }
+
+ $isSafe = isset($options['safe']) || isset($options['w']);
+
+ $result =
+ $this->db
+ ->selectCollection($table)
+ ->batchInsert($rows, $options);
+
+ if ($isSafe && is_array($result)) {
+ $this->checkResult($result);
+ }
+
+ return $result;
+ }
+
+ public function update($table, array $row, $options = array()) {
+ $row = $this->encodeId($row);
+ $id = isset($row['_id']) ? $row['_id'] : null;
+ //unset($row['_id']);
+ $options = array_merge(
+ array('safe' => true),
+ $options
+ );
+ if ($options['safe']) {
+ if ($this->checkVersion('1.3.0')) {
+ $options['w'] = $this->writeConcern;
+ unset($options['safe']);
+ } else {
+ $options['safe'] = $this->writeConcern;
+ }
+ }
+
+ $isSafe = isset($options['safe']) || isset($options['w']);
+
+ if (isset($options['where'])) {
+ if (is_array($options['where'])) {
+ $where = $options['where'];
+ }
+ unset($options['where']);
+
+ } else if ($id !== null) {
+ $where = array('_id' => $id);
+ }
+
+ if (empty($where)) {
+ throw new NoSQLException('empty "where" clause for update');
+ }
+
+ try {
+
+ $result =
+ $this
+ ->db
+ ->selectCollection($table)
+ ->update($where, $row, $options);
+
+ if ($isSafe && is_array($result)) {
+ $this->checkResult($result);
+ if (isset($result['upserted'])) {
+ $upserted = $result['upserted'];
+ if (is_array($upserted)) {
+ /**
+ * in mongo >=2.6 with driver <1.5.3 we would get an array of ids
+ * @see https://jira.mongodb.org/browse/PHP-1109
+ */
+ $upserted = array_pop($upserted);
+ }
+ if ($upserted instanceof MongoId) {
+ $id = $upserted;
+ }
+ }
+ }
+
+ } catch (Exception $e) {
+ if ($e instanceof MongoCursorTimeoutException && !$this->isRetrying) {
+ $this->reconnectAndRetry(__FUNCTION__, func_get_args());
+ } else {
+ throw $e;
+ }
+ }
+
+ $row['_id'] = $id;
+ // return clean row
+ return $this->decodeId($row);
+ }
+
+ protected function checkResult($result) {
+ if (!isset($result['ok']) || $result['ok'] == 0) {
+ $code = isset($result['code']) ? $result['code'] : 0;
+ $message = '';
+ if (isset($result['err'])) {
+ $message .= 'err: ' . $result['err'] . '. ';
+ }
+ if (isset($result['errmsg'])) {
+ $message .= 'errmsg: ' . $result['errmsg'] . '. ';
+ }
+ throw new MongoException($message, $code);
+ }
+ }
+
+ public function deleteOne($table, $key) {
+ return
+ $this
+ ->db
+ ->selectCollection($table)
+ ->remove( array('_id' => $this->makeId($key)), array('justOne' => true) );
+ }
+
+ public function deleteList($table, array $keys) {
+ return
+ $this
+ ->db
+ ->selectCollection($table)
+ ->remove( array('_id' => array('$in' => $this->makeIdList($keys))) );
+ }
+
+ public function getPlainList($table) {
+ // quering
+ $cursor =
+ $this
+ ->db
+ ->selectCollection($table)
+ ->find();
+ // recieving objects
+ $rows = array();
+ foreach ($cursor as $row) {
+ $rows[] = $this->decodeId($row);
+ }
+ // return result
+ return $rows;
+ }
+
+ public function getTotalCount($table) {
+ return
+ $this
+ ->db
+ ->selectCollection($table)
+ ->find(array(), array('_id'))
+ ->count();
+ }
+
+ public function getCountByField($table, $field, $value, Criteria $criteria = null) {
+ if( Assert::checkInteger($value) ) {
+ $value = (int)$value;
+ }
+ $options = $this->parseCriteria($criteria);
+
+ return
+ $this->mongoCount($table, array($field => $value), array('_id'), $options[self::C_ORDER], $options[self::C_LIMIT], $options[self::C_SKIP]);
+ }
+
+ public function getListByField($table, $field, $value, Criteria $criteria = null) {
+ if( Assert::checkInteger($value) ) {
+ $value = (int)$value;
+ }
+ $options = $this->parseCriteria($criteria);
+
+ return
+ $this->mongoFind($table, array($field => $value), $options[self::C_FIELDS], $options[self::C_ORDER], $options[self::C_LIMIT], $options[self::C_SKIP]);
+ }
+
+ public function getIdListByField($table, $field, $value, Criteria $criteria = null) {
+ if( Assert::checkInteger($value) ) {
+ $value = (int)$value;
+ }
+ $options = $this->parseCriteria($criteria);
+
+ return
+ $this->mongoFind($table, array($field => $value), array('_id'), $options[self::C_ORDER], $options[self::C_LIMIT], $options[self::C_SKIP]);
+ }
+
+ public function find($table, $query) {
+ return
+ $this->mongoFind($table, $query);
+ }
+
+ public function findByCriteria(Criteria $criteria) {
+ $options = $this->parseCriteria($criteria);
+
+ if( !isset($options[self::C_TABLE]) ) {
+ throw new NoSQLException('Can not find without table!');
+ }
+// if( !isset($options[self::C_QUERY]) ) {
+// throw new NoSQLException('Can not find without query!');
+// }
+
+ return
+ $this->mongoFind($options[self::C_TABLE], $options[self::C_QUERY], $options[self::C_FIELDS], $options[self::C_ORDER], $options[self::C_LIMIT], $options[self::C_SKIP]);
+ }
+
+ public function countByCriteria(Criteria $criteria) {
+ $options = $this->parseCriteria($criteria);
+
+ if( !isset($options[self::C_TABLE]) ) {
+ throw new NoSQLException('Can not find without table!');
+ }
+// if( !isset($options[self::C_QUERY]) ) {
+// throw new NoSQLException('Can not find without query!');
+// }
+
+ return
+ $this->mongoCount($options[self::C_TABLE], $options[self::C_QUERY], array(), $options[self::C_ORDER], $options[self::C_LIMIT], $options[self::C_SKIP]);
+ }
+
+ public function deleteByCriteria(Criteria $criteria, array $options = array('safe' => true)) {
+ $query = $this->parseCriteria($criteria);
+
+ if( !isset($query[self::C_TABLE]) ) {
+ throw new NoSQLException('Can not find without table!');
+ }
+
+ // extend options
+ $options = array_merge(
+ array('safe' => true),
+ $options
+ );
+
+ if ($options['safe']) {
+ if ($this->checkVersion('1.3.0')) {
+ $options['w'] = $this->writeConcern;
+ unset($options['safe']);
+ } else {
+ $options['safe'] = $this->writeConcern;
+ }
+ }
+
+ $this->mongoDelete($query[self::C_TABLE], $query[self::C_QUERY], $options);
+ }
+
+ /**
+ * @param Criteria $criteria
+ * @return MongoCursor
+ * @throws NoSQLException
+ */
+ public function makeCursorByCriteria(Criteria $criteria) {
+ $options = $this->parseCriteria($criteria);
+
+ if (!isset($options[self::C_TABLE])) {
+ throw new NoSQLException('Can not find without table!');
+ }
+
+ return $this->mongoMakeCursor(
+ $options[self::C_TABLE],
+ $options[self::C_QUERY],
+ $options[self::C_FIELDS],
+ $options[self::C_ORDER],
+ $options[self::C_LIMIT],
+ $options[self::C_SKIP]
+ );
+ }
+
+ protected function mongoFind($table, array $query, array $fields=array(), array $order=null, $limit=null, $skip=null) {
+ // quering
+ $cursor = $this->mongoMakeCursor($table, $query, $fields, $order, $limit, $skip);
+ // recieving objects
+ $rows = array();
+ foreach ($cursor as $row) {
+ $rows[] = $this->decodeId($row);
+ }
+ // return result
+ return $rows;
+ }
+
+ protected function mongoCount($table, array $query, array $fields=array(), array $order=null, $limit=null, $skip=null) {
+ // quering
+ $cursor = $this->mongoMakeCursor($table, $query, $fields, $order, $limit, $skip);
+ // fetch result
+ $count = $cursor->count();
+ // check result
+ self::assertCountResult($count);
+ // return count
+ return $count;
+ }
+
+ public static function assertCountResult($count) {
+ if (!Assert::checkInteger($count) || $count < 0) {
+ if (is_array($count)) {
+ $code = isset($count['code']) ? $count['code'] : null;
+ $text = isset($count['errmsg']) ? $count['errmsg'] : json_encode($count);
+ throw new MongoCursorException($text, $code);
+ } else {
+ throw new UnexpectedValueException($count);
+ }
+ }
+ }
+
+ protected function mongoDelete($table, array $query, array $options) {
+ $res = $this->db->selectCollection($table)->remove($query, $options);
+ if (isset($res['err']) && !is_null($res['err'])) {
+ throw new NoSQLException($res['err']);
+ }
+ }
+
+ /**
+ * @param $table
+ * @param array $query
+ * @param array $fields
+ * @param array $order
+ * @param int $limit
+ * @param int $skip
+ * @return MongoCursor
+ */
+ protected function mongoMakeCursor($table, array $query, array $fields=array(), array $order=null, $limit=null, $skip=null) {
+ $cursor =
+ $this
+ ->db
+ ->selectCollection($table)
+ ->find( $query, $fields );
+ if( !is_null($order) ) {
+ $cursor->sort( $order );
+ }
+ if( !is_null($limit) ) {
+ $cursor->limit( $limit );
+ }
+ if( !is_null($skip) ) {
+ $cursor->skip( $skip );
+ }
+ return $cursor;
+ }
+
+ /**
+ * @param string $table
+ * @param string $map
+ * @param string $reduce
+ * @param Criteria $criteria
+ * @param int $timeout
+ * @param array $out
+ * @throws NoSQLException
+ * @return array
+ */
+ public function mapReduce($table, $map, $reduce, Criteria $criteria=null, $timeout=30, $out=array('inline'=>1)) {
+ $options = $this->parseCriteria($criteria);
+
+ $command = array(
+ 'mapreduce' => $table,
+ 'map' => new MongoCode($map),
+ 'reduce' => new MongoCode($reduce),
+ 'out' => $out
+ );
+ // обрабатываем критерию
+ if( !empty($options[self::C_QUERY]) ) {
+ $command['query'] = $options[self::C_QUERY];
+ }
+ if( !empty($options[self::C_ORDER]) ) {
+ $command['sort'] = $options[self::C_ORDER];
+ }
+ if( !empty($options[self::C_LIMIT]) ) {
+ $command['limit'] = $options[self::C_LIMIT];
+ }
+
+ $result = $this->db->command($command, array('timeout'=>$timeout*1000));
+
+ // обрабатываем результаты
+ $list = array();
+ if( is_array($result) && isset($result['ok']) && $result['ok']==1 ) {
+ if (isset($result['results'])) {
+ foreach( $result['results'] as $row ) {
+ // prepare id
+ $row['id'] = $row['_id'];
+ unset($row['_id']);
+ // prepare values
+ foreach($row['value'] as $key=>$value) {
+ $row[$key] = is_bool($value) ? (int)$value : $value;
+ }
+ unset($row['value']);
+
+ $list[ $row['id'] ] = $row;
+ }
+ } else {
+ $list = $result;
+ }
+ } else {
+ throw new NoSQLException('Error during map/reduce running');
+ }
+ return $list;
+ }
+
+ public function increment($table, array $fields, Criteria $criteria = null) {
+ return null;
+ }
+
+/// helper functions
+//@{
+ /**
+ * Encode ID to MongoId
+ * @param array $row
+ * @return array
+ */
+ protected function encodeId(array $row) {
+ if( isset($row['id']) ) {
+ $row['_id'] = $this->makeId($row['id']);
+ }
+ unset($row['id']);
+ return $row;
+ }
+
+ /**
+ * Decode ID from MongoId to string
+ * @param array $row
+ * @return array
+ */
+ protected function decodeId(array $row) {
+ $row['id'] = (string)$row['_id'];
+ unset($row['_id']);
+ return $row;
+ }
+
+ protected function makeId($key) {
+ return ($key instanceof MongoId) ? $key : new MongoId($key);
+ }
+
+ protected function makeIdList(array $keys) {
+ $fields = array();
+ foreach( $keys as $key ) {
+ //$fields[] = array( '_id'=>$this->makeId($key) );
+ $fields[] = $this->makeId($key);
+ }
+ return $fields;
+ }
+
+ /**
+ * Разбираем критерию на параметры запроса к монго
+ * @param Criteria $criteria
+ * @return array
+ */
+ protected function parseCriteria(Criteria $criteria=null) {
+ $result = array();
+ // парсим табличку
+ if( !is_null($criteria) && $criteria->getDao() ) {
+ $result[self::C_TABLE] = $criteria->getDao()->getTable();
+ } else {
+ $result[self::C_TABLE] = null;
+ }
+ // парсим запросы
+ if( !is_null($criteria) && $criteria->getLogic()->getLogic() ) {
+ $logic = $criteria->getLogic()->getChain();
+ $expression = array_shift($logic);
+ if( $expression instanceof NoSQLExpression ) {
+ $result[self::C_FIELDS] = $expression->getFieldList();
+ $result[self::C_QUERY] = $expression->toMongoQuery();
+ } else {
+ $result[self::C_FIELDS] = array();
+ $result[self::C_QUERY] = array();
+ }
+ } else {
+ $result[self::C_FIELDS] = array();
+ $result[self::C_QUERY] = array();
+ }
+ // парсим сортировку
+ if( !is_null($criteria) && $criteria->getOrder() ) {
+ /** @var $order OrderBy */
+ $order = $criteria->getOrder()->getLast();
+ if( $order instanceof OrderBy ) {
+ $result[self::C_ORDER] = array($order->getFieldName() => $order->isAsc()?1:-1);
+ } else {
+ $result[self::C_ORDER] = null;
+ }
+ } else {
+ $result[self::C_ORDER] = null;
+ }
+ // парсим лимит
+ if( !is_null($criteria) && $criteria->getLimit() ) {
+ $result[self::C_LIMIT] = $criteria->getLimit();
+ } else {
+ $result[self::C_LIMIT] = null;
+ }
+ // парсим сдвиг
+ if( !is_null($criteria) && $criteria->getOffset() ) {
+ $result[self::C_SKIP] = $criteria->getOffset();
+ } else {
+ $result[self::C_SKIP] = null;
+ }
+ // отдаем результат
+ return $result;
+ }
+
+ /**
+ * Возвращает актуальное имя класса клиента
+ * @return string
+ */
+ public static function getClientClass() {
+ try {
+ Assert::classExists('MongoClient');
+ return 'MongoClient';
+ } catch( Exception $e ) {
+ return 'Mongo';
+ }
+ }
+
+ /**
+ * Проверяет, что драйвер соответствует или новее версии $lowest
+ * @param string $lowest версия в виде "1.2.3"
+ * @return boolean
+ */
+ public static function checkVersion($lowest) {
+ $Mongo = self::getClientClass();
+ try {
+ $version = constant($Mongo . '::VERSION');
+ } catch (BaseException $e) {
+ return false;
+ }
+ return version_compare($version, $lowest, '>=');
+ }
+
+//@}
+}
diff --git a/core/NoSQL/NoSQL.class.php b/core/NoSQL/NoSQL.class.php
new file mode 100755
index 0000000000..7d1628e3f2
--- /dev/null
+++ b/core/NoSQL/NoSQL.class.php
@@ -0,0 +1,87 @@
+1));
+
+ public function getTableInfo($table) {
+ throw new UnsupportedMethodException('Can not execute getTableInfo in NoSQL');
+ }
+
+ public function queryRaw($queryString) {
+ throw new UnsupportedMethodException('Can not execute queryRaw in NoSQL');
+ }
+
+ public function queryRow(Query $query) {
+ throw new UnsupportedMethodException('Can not execute queryRow in NoSQL');
+ }
+
+ public function querySet(Query $query) {
+ throw new UnsupportedMethodException('Can not execute querySet in NoSQL');
+ }
+
+ public function queryNumRows(Query $query) {
+ throw new UnsupportedMethodException('Can not execute queryNumRows in NoSQL');
+ }
+
+ public function queryColumn(Query $query) {
+ throw new UnsupportedMethodException('Can not execute queryColumn in NoSQL');
+ }
+
+ public function queryCount(Query $query) {
+ throw new UnsupportedMethodException('Can not execute queryCount in NoSQL');
+ }
+
+ public function setDbEncoding() {
+ throw new UnsupportedMethodException('Can not set encoding in NoSQL');
+ }
+
+}
diff --git a/core/NoSQL/NoSqlDAO.class.php b/core/NoSQL/NoSqlDAO.class.php
new file mode 100755
index 0000000000..88af15ed7b
--- /dev/null
+++ b/core/NoSQL/NoSqlDAO.class.php
@@ -0,0 +1,549 @@
+getLink()->selectOne( $this->getTable(), $id )) {
+ $object = $this->makeNoSqlObject($row);
+ } else {
+ throw new ObjectNotFoundException( 'Object with id '.$id.' does not exist' );
+ }
+ return $object;
+ }
+
+ /**
+ * @param LogicalObject $logic
+ * @param int $expires
+ * @return Identifiable|Prototyped
+ * @throws ObjectNotFoundException|UnimplementedFeatureException|WrongArgumentException
+ */
+ public function getByLogic(LogicalObject $logic, $expires = Cache::DO_NOT_CACHE) {
+ if( !($logic instanceof NoSQLExpression) ) {
+ throw new WrongArgumentException( '$logic should be instance of NoSQLExpression' );
+ }
+ // quering for different NoSQL types
+ $rows = array();
+ if( $this->getLink() instanceof MongoBase ) {
+ $rows = $this->getLink()->find($this->getTable(), $logic->toMongoQuery());
+ } else {
+ throw new UnimplementedFeatureException( 'Method "getByLogic" is not implemented now for your NoSQL DB' );
+ }
+ // processing list
+ if( count($rows)==0 ) {
+ throw new ObjectNotFoundException('Can not find object for your query');
+ } else {
+ return $this->makeNoSqlObject( array_shift($rows) );
+ }
+ }
+
+ /**
+ * @param SelectQuery $query
+ * @param int $expires
+ * @throws UnsupportedMethodException
+ */
+ public function getByQuery(SelectQuery $query, $expires = Cache::DO_NOT_CACHE) {
+ throw new UnsupportedMethodException( 'Can not execute "getByQuery" in NoSQL' );
+ }
+
+ /**
+ * @param SelectQuery $query
+ * @param int $expires
+ * @throws UnsupportedMethodException
+ */
+ public function getCustom(SelectQuery $query, $expires = Cache::DO_NOT_CACHE) {
+ throw new UnsupportedMethodException( 'Can not execute "getCustom" in NoSQL' );
+ }
+//@}
+
+/// object's list getters
+//@{
+ /**
+ * @param array $ids
+ * @param int $expires
+ * @return array
+ */
+ public function getListByIds(array $ids, $expires = Cache::EXPIRES_MEDIUM) {
+ $list = array();
+ $rows = $this->getLink()->selectList( $this->getTable(), $ids );
+ foreach($rows as $row) {
+ $list[] = $this->makeNoSqlObject($row);
+ }
+ return $list;
+ }
+
+ /**
+ * @param SelectQuery $query
+ * @param int $expires
+ * @throws UnsupportedMethodException
+ */
+ public function getListByQuery(SelectQuery $query, $expires = Cache::DO_NOT_CACHE) {
+ throw new UnsupportedMethodException( 'Can not execute "getListByQuery" in NoSQL' );
+ }
+
+ /**
+ * @param LogicalObject $logic
+ * @param int $expires
+ * @return array
+ * @throws UnimplementedFeatureException|WrongArgumentException
+ */
+ public function getListByLogic(LogicalObject $logic, $expires = Cache::DO_NOT_CACHE) {
+ if( !($logic instanceof NoSQLExpression) ) {
+ throw new WrongArgumentException( '$logic should be instance of NoSQLExpression' );
+ }
+ // quering for different NoSQL types
+ $rows = array();
+ if( $this->getLink() instanceof MongoBase ) {
+ $rows = $this->getLink()->find($this->getTable(), $logic->toMongoQuery());
+ } else {
+ throw new UnimplementedFeatureException( 'Method "getByLogic" is not implemented now for your NoSQL DB' );
+ }
+ // processing list
+ $list = array();
+ foreach($rows as $row) {
+ $list[] = $this->makeNoSqlObject($row);
+ }
+ return $list;
+ }
+
+ /**
+ * @param Criteria $criteria
+ * @param int $expires
+ * @return array
+ */
+ public function getListByCriteria(Criteria $criteria, $expires = Cache::DO_NOT_CACHE) {
+ $criteria->setDao( $this );
+ // getting list
+ $list = array();
+ $stack = $this->getLink()->findByCriteria($criteria);
+ foreach( $stack as $row ) {
+ $object = $this->makeNoSqlObject($row);
+ $list[ $object->getId() ] = $object;
+ }
+ return $list;
+ }
+
+ /**
+ * @param Criteria $criteria
+ * @param int $expires
+ * @return int
+ */
+ public function getCountByCriteria(Criteria $criteria, $expires = Cache::DO_NOT_CACHE) {
+ $criteria->setDao( $this );
+ return $this->getLink()->countByCriteria($criteria);
+ }
+
+ /**
+ * @param int $expires
+ * @return array
+ */
+ public function getPlainList($expires = Cache::EXPIRES_MEDIUM) {
+ $list = array();
+ $stack = $this->getLink()->getPlainList( $this->getTable() );
+ foreach( $stack as $row ) {
+ $object = $this->makeNoSqlObject($row);
+ $list[ $object->getId() ] = $object;
+ }
+
+ return $list;
+ }
+
+ /**
+ * @param int $expires
+ * @return int
+ */
+ public function getTotalCount($expires = Cache::DO_NOT_CACHE) {
+ return $this->getLink()->getTotalCount( $this->getTable() );
+ }
+//@}
+
+
+/// custom list getters
+//@{
+ public function getCustomList(SelectQuery $query, $expires = Cache::DO_NOT_CACHE) {
+ throw new UnsupportedMethodException( 'Can not execute "getCustomList" in NoSQL' );
+ }
+
+ public function getCustomRowList(SelectQuery $query, $expires = Cache::DO_NOT_CACHE) {
+ throw new UnsupportedMethodException( 'Can not execute "getCustomRowList" in NoSQL' );
+ }
+//@}
+
+/// query result getters
+//@{
+ public function getQueryResult(SelectQuery $query, $expires = Cache::DO_NOT_CACHE) {
+ throw new UnsupportedMethodException( 'Can not execute "getQueryResult" in NoSQL' );
+ }
+
+ public function getNoSqlResult(Criteria $criteria, $expires = Cache::DO_NOT_CACHE) {
+ $criteria->setDao( $this );
+ $cursor = $this->getLink()->makeCursorByCriteria($criteria);
+ return NoSqlResult::create()
+ ->setCriteria($criteria)
+ ->setDao($this)
+ ->setMongoCursor($cursor);
+ }
+//@}
+
+/// some queries
+//@{
+ public function makeSelectHead() {
+ throw new UnsupportedMethodException( 'Method "makeSelectHead" is not supported in NoSQL' );
+ }
+
+ public function makeTotalCountQuery() {
+ throw new UnsupportedMethodException( 'Method "makeTotalCountQuery" is not supported in NoSQL' );
+ }
+//@}
+
+/// erasers
+//@{
+ public function drop(Identifiable $object) {
+ $this->assertNoSqlObject( $object );
+ return $this->dropById( $object->getId() );
+ }
+
+ public function dropById($id) {
+ $this->runTrigger($id, 'onBeforeDrop');
+ $after = $this->prepareTrigger($id, 'onAfterDrop');
+
+ $link = NoSqlPool::getByDao( $this );
+ $result = $link->deleteOne($this->getTable(), $id);
+
+ call_user_func($after);
+
+ return $result;
+ }
+
+ public function dropByIds(array $ids) {
+ $this->runTrigger($ids, 'onBeforeDrop');
+ $after = $this->prepareTrigger($ids, 'onAfterDrop');
+
+ $link = NoSqlPool::getByDao( $this );
+
+ $result = $link->deleteList($this->getTable(), $ids);
+
+ call_user_func($after);
+
+ return $result;
+ }
+
+ public function dropByCriteria(Criteria $criteria) {
+ $criteria->setDao($this);
+ $link = $this->getLink();
+ if ($link instanceof MongoBase) {
+ /** @var $link MongoBase */
+ $link->deleteByCriteria($criteria);
+ } else {
+ throw new WrongStateException('only available in MongoBase NoSqlDAO');
+ }
+ }
+//@}
+
+/// injects
+//@{
+ protected function inject( InsertOrUpdateQuery $query, Identifiable $object) {
+ throw new UnsupportedMethodException( 'Method "inject" is not supported in NoSQL' );
+ }
+
+ protected function doInject( InsertOrUpdateQuery $query, Identifiable $object) {
+ throw new UnsupportedMethodException( 'Method "doInject" is not supported in NoSQL' );
+ }
+//@}
+
+/// savers
+//@{
+ public function take(Identifiable $object) {
+ return
+ $object->getId()
+ ? $this->merge($object, true)
+ : $this->add($object);
+ }
+
+ /**
+ * @param NoSqlObject[] $objectList
+ * @return AbstractAmqpObject|mixed|null
+ */
+ public function multiAdd(array $objectList) {
+ $rows = array();
+ $objectList = array_values($objectList);
+ foreach( $objectList as $object ) {
+ $this->assertNoSqlObject( $object );
+ // преобразуем объект в массив для nosql
+ $rows[] = $object->toArray();
+ }
+
+ if( !empty($rows) ) {
+ $link = NoSqlPool::getByDao( $this );
+ // insert
+ $entityList =
+ $link
+ ->batchInsert(
+ $this->getTable(),
+ $rows
+ );
+ foreach($entityList as $key=>$entity) {
+ $object = $objectList[$key];
+ $object->setId($entity['id']);
+ }
+ }
+
+ // проверяем наличие ИДешек
+ foreach($objectList as &$object) {
+ if(!$object->getId()) {
+ unset($object);
+ }
+ }
+
+ return $objectList;
+ }
+
+
+ public function addUnsafe(NoSqlObject $object) {
+ return $this->doAdd($object, false);
+ }
+
+ public function add(Identifiable $object) {
+ $this->assertNoSqlObject( $object );
+ return $this->doAdd($object, true);
+ }
+
+ protected function doAdd(NoSqlObject $object, $safe = true) {
+ $this->checkNoSqlObject($object);
+
+ $this->runTrigger($object, 'onBeforeSave');
+
+ $row = NoSqlPool::getByDao($this)
+ ->insert(
+ $this->getTable(),
+ $object->toArray(),
+ array('safe' => $safe)
+ );
+
+ $object->setId($row['id']);
+
+ $this->runTrigger($object, 'onAfterSave');
+
+ return $object;
+ }
+
+ public function saveUnsafe(NoSqlObject $object) {
+ return $this->doSave($object, false);
+ }
+
+ public function save(Identifiable $object) {
+ $this->assertNoSqlObject( $object );
+ return $this->doSave($object, true);
+ }
+
+ protected function doSave(NoSqlObject $object, $safe = true) {
+ $this->checkNoSqlObject($object);
+
+ $this->runTrigger($object, 'onBeforeSave');
+
+ $row = NoSqlPool::getByDao($this)
+ ->update(
+ $this->getTable(),
+ $object->toArray(),
+ array('safe' => $safe)
+ );
+
+ $this->runTrigger($object, 'onAfterSave');
+
+ //$object->setId($row['id']);
+
+ return $object;
+ }
+
+ public function import(Identifiable $object) {
+ return $this->save($object);
+ }
+
+ public function merge(Identifiable $object, $cacheOnly = true) {
+ Assert::isNotNull($object->getId());
+
+ $this->checkObjectType($object);
+
+ try {
+ $old = $this->getById($object->getId());
+ } catch( Exception $e ) {
+ return $this->save($object);
+ }
+
+ return $this->unite($object, $old);
+ }
+
+ public function unite( Identifiable $object, Identifiable $old ) {
+ Assert::isNotNull($object->getId());
+
+ Assert::isTypelessEqual(
+ $object->getId(), $old->getId(),
+ 'cannot merge different objects'
+ );
+
+ $hasChanges = false;
+
+ foreach ($this->getProtoClass()->getPropertyList() as $property) {
+ $getter = $property->getGetter();
+
+ if ($property->getClassName() === null) {
+ $changed = ($old->$getter() !== $object->$getter());
+ } else {
+ /**
+ * way to skip pointless update and hack for recursive
+ * comparsion.
+ **/
+ $changed =
+ ($old->$getter() !== $object->$getter())
+ || ($old->$getter() != $object->$getter());
+ }
+
+ if ($changed) {
+ $hasChanges = true;
+ }
+ }
+
+ if( $hasChanges ) {
+ $object = $this->save( $object );
+ }
+
+ return $object;
+ }
+//@}
+
+
+/// object's list getters by foreign_key
+//@{
+ public function getOneByField($field, $value, Criteria $criteria = null) {
+ if( is_null($criteria) ) {
+ $criteria = Criteria::create();
+ }
+ $criteria->setLimit(1);
+ // get object
+ $list = $this->getListByField( $field, $value, $criteria );
+ if( empty($list) ) {
+ throw new ObjectNotFoundException();
+ }
+ return array_shift($list);
+ }
+
+ public function getListByField($field, $value, Criteria $criteria = null) {
+ $list = array();
+ $rows = $this->getLink()->getListByField( $this->getTable(), $field, $value, $criteria );
+ foreach($rows as $row) {
+ $list[] = $this->makeNoSqlObject($row);
+ }
+ return $list;
+ }
+
+ public function getIdListByField($field, $value, Criteria $criteria = null) {
+ $list = array();
+ $rows = $this->getLink()->getIdListByField( $this->getTable(), $field, $value, $criteria );
+ foreach($rows as $row) {
+ $list[] = $row['id'];
+ }
+ return $list;
+ }
+
+ public function getCountByField($field, $value, Criteria $criteria = null) {
+ return $this->getLink()->getCountByField( $this->getTable(), $field, $value, $criteria );
+ }
+//@}
+
+/// map/reduce
+//@{
+ public function mapReduce($map, $reduce, Criteria $criteria=null, $timeout=30, $out=array('inline'=>1)) {
+ return $this->getLink()->mapReduce( $this->getTable(), $map, $reduce, $criteria, $timeout, $out );
+ }
+
+ public function increment($field, $value, $criteria) {
+ return $this->getLink()->increment( $this->getTable(), $field, $value, $criteria );
+ }
+//@}
+
+ /**
+ * @param $object
+ * @throws WrongStateException
+ */
+ protected function assertNoSqlObject( $object ) {
+ if( !($object instanceof NoSqlObject) ) {
+ throw new WrongStateException('Object must be instance of NoSqlObject');
+ }
+ }
+
+ /**
+ * @param Prototyped $object
+ * @return bool
+ * @throws NoSQLException
+ */
+ protected function checkNoSqlObject(Prototyped $object) {
+ $form = Form::create();
+ foreach ($object->proto()->getPropertyList() as $property) {
+ /** @var $property LightMetaProperty */
+ if ($property->isIdentifier() || $property->getRelationId() > MetaRelation::ONE_TO_ONE) {
+ continue;
+ }
+ if ($property->getType() == 'scalarIdentifier') {
+ $form->add(
+ Primitive::string($property->getColumnName())
+ ->setRequired($property->isRequired())
+ );
+ } else if ($property->getType() == 'integerIdentifier') {
+ $form->add(
+ Primitive::integer($property->getColumnName())
+ ->setRequired($property->isRequired())
+ );
+ } else {
+ $form->add($property->makePrimitive($property->getColumnName()));
+ }
+ }
+
+ $form->import(PrototypeUtils::toArray($object));
+ if( $form->getErrors() ) {
+ throw new NoSQLException( 'Object does not have all required fields: '.var_export($form->getErrors(), true) );
+ }
+
+ return true;
+ }
+
+ /**
+ * @param array $row
+ * @param null $prefix
+ * @throws WrongStateException
+ * @return Identifiable|Prototyped
+ */
+ public function makeNoSqlObject($row, $prefix = null) {
+ $object = null;
+ try {
+ $object = $this->makeObject( $row, $prefix );
+ } catch(Exception $e) {
+ throw new WrongStateException( 'Can not make object with id '.$row['id'].'. Dump: '.var_export($row, true) );
+ }
+ return $object;
+ }
+
+ /**
+ * @return NoSQL
+ */
+ public function getLink() {
+ return NoSqlPool::me()->getLink( $this->getLinkName() );
+ }
+
+}
diff --git a/core/NoSQL/NoSqlObject.class.php b/core/NoSQL/NoSqlObject.class.php
new file mode 100755
index 0000000000..563f3c2a2f
--- /dev/null
+++ b/core/NoSQL/NoSqlObject.class.php
@@ -0,0 +1,27 @@
+getLink($dao->getLinkName());
+ }
+
+ /**
+ * @param NoSQL $db
+ * @return NoSqlPool
+ */
+ public function setDefault(NoSQL $db)
+ {
+ $this->default = $db;
+
+ return $this;
+ }
+
+ /**
+ * @return NoSqlPool
+ **/
+ public function dropDefault()
+ {
+ $this->default = null;
+
+ return $this;
+ }
+
+ /**
+ * @param string $name
+ * @param NoSQL $db
+ * @return NoSqlPool
+ * @throws WrongArgumentException
+ */
+ public function addLink($name, NoSQL $db)
+ {
+ if (isset($this->pool[$name]))
+ throw new WrongArgumentException(
+ "already have '{$name}' link"
+ );
+
+ $this->pool[$name] = $db;
+
+ return $this;
+ }
+
+ /**
+ * @param string $name
+ * @return NoSqlPool
+ * @throws MissingElementException
+ */
+ public function dropLink($name)
+ {
+ if (!isset($this->pool[$name]))
+ throw new MissingElementException(
+ "link '{$name}' not found"
+ );
+
+ unset($this->pool[$name]);
+
+ return $this;
+ }
+
+ /**
+ * @param string $name
+ * @return NoSQL
+ * @throws MissingElementException
+ */
+ public function getLink($name = null)
+ {
+ /** @var $link NoSQL */
+ $link = null;
+
+ // single-NoSQL project
+ if (!$name) {
+ if (!$this->default) {
+ throw new MissingElementException(
+ 'i have no default link and requested link name is null'
+ );
+ }
+
+ $link = $this->default;
+ } elseif (isset($this->pool[$name])) {
+ $link = $this->pool[$name];
+ }
+ // check if found and return
+ if ($link) {
+ if (!$link->isConnected())
+ $link->connect();
+
+ return $link;
+ }
+
+ throw new MissingElementException(
+ "can't find link with '{$name}' name"
+ );
+ }
+
+ /**
+ * @return NoSqlPool
+ */
+ public function shutdown()
+ {
+ $this->default = null;
+ $this->pool = array();
+
+ return $this;
+ }
+}
+?>
\ No newline at end of file
diff --git a/core/NoSQL/NoSqlResult.class.php b/core/NoSQL/NoSqlResult.class.php
new file mode 100644
index 0000000000..24f4129af6
--- /dev/null
+++ b/core/NoSQL/NoSqlResult.class.php
@@ -0,0 +1,186 @@
+
+ * @date 2012.07.02
+ */
+class NoSqlResult extends QueryResult {
+ /** @var NoSqlDAO */
+ protected $dao;
+
+ /** @var MongoCursor */
+ protected $mongoCursor;
+
+ /** @var NoSqlResultList */
+ protected $resultList;
+
+ /** @var Criteria */
+ protected $criteria;
+
+ protected $count = null;
+
+ /**
+ * @static
+ * @param MongoCursor $cursor
+ * @return NoSqlResult
+ */
+ public static function create() {
+ return new static;
+ }
+
+
+ public function setCriteria(Criteria $criteria) {
+ $this->criteria = $criteria;
+ return $this;
+ }
+
+ public function getCriteria() {
+ return $this->criteria;
+ }
+
+ public function getCount() {
+ if ($this->count == null && $this->getMongoCursor()) {
+ $count = $this->getMongoCursor()->count();
+ MongoBase::assertCountResult($count);
+
+ $this->count = $count;
+
+ /* -- плохой вариант, долго считает
+ // пытаемся посчитать количество записей перебором, без использования count()
+ // откидиываем limit и offset
+ $criteria = clone $this->getCriteria();
+ $criteria
+ ->setLimit(null)
+ ->setOffset(null);
+ // находим в логике NoSQLExpression
+ $expression = null;
+ foreach ($criteria->getLogic()->getChain() as $logic) {
+ if ($logic instanceof NoSQLExpression) {
+ $expression = $logic;
+ }
+ }
+
+ // если нашли - делаем запрос
+ if ($expression instanceof NoSQLExpression) {
+ // берем первое попавшееся в запросе поле и запрашиваем только его
+ foreach ($expression->toMongoQuery() as $field => $condition) {
+ if ($field[0] != '$') {
+ $expression->addField(array($field));
+ break;
+ }
+ }
+
+ $timeStart = microtime(1);
+ $timeMax = Config::me()->getMongoTimeout() / 2;
+ $this->count = 0;
+ $db = NoSqlPool::me()->getByDao($this->getDao());
+ $cursor = $db->makeCursorByCriteria($criteria);
+ // пересчитываем количество выбранных записей
+ foreach ($cursor as $item) {
+ $this->count++;
+
+ // следим за таймаутом, т.к. время запроса к базе не учитывается
+ // в max_execution_time
+ if (microtime(1) - $timeStart > $timeMax) {
+ throw new MongoCursorTimeoutException(
+ 'failed to count in ' . $timeMax . ' seconds: '
+ . json_encode($expression->toMongoQuery())
+ );
+ }
+ }
+
+ } else {
+ $this->count = $this->getMongoCursor()->count();
+ }
+ */
+ }
+ return $this->count;
+ }
+
+
+ /**
+ * @return NoSqlResultList
+ */
+ public function getList() {
+ return $this->resultList;
+ }
+
+ /**
+ * @param $resultList
+ * @return NoSqlResult
+ * @throws WrongArgumentException
+ */
+ public function setList($resultList) {
+ if (!($resultList instanceof NoSqlResultList)) {
+ throw new WrongArgumentException('NoSqlResult accepts only NoSqlResultList in setList');
+ }
+ $this->resultList = $resultList;
+ return $this;
+ }
+
+ /**
+ * @param MongoCursor $cursor
+ * @return NoSqlResult
+ */
+ public function setMongoCursor(MongoCursor $cursor) {
+ $this->mongoCursor = $cursor;
+ $this
+ ->setList(NoSqlResultList::create($this))
+ ->setCount(null); // lazy
+ return $this;
+ }
+
+ /**
+ * @return MongoCursor
+ */
+ public function getMongoCursor() {
+ return $this->mongoCursor;
+ }
+
+ /**
+ * @param NoSqlDAO $dao
+ * @return NoSqlResult
+ */
+ public function setDao(NoSqlDAO $dao) {
+ $this->dao = $dao;
+ return $this;
+ }
+
+ /**
+ * @return NoSqlDAO
+ */
+ public function getDao() {
+ return $this->dao;
+ }
+
+ /**
+ * @return null|string
+ */
+ public function getId() {
+ if ($this->getMongoCursor() == null) {
+ return null;
+ }
+ $queryInfo = $this->getMongoCursor()->info();
+ return '_result_nosql_' . sha1(json_encode($queryInfo['query']));
+ }
+
+ public function setQuery(SelectQuery $query) {
+ throw new UnimplementedFeatureException('NoSqlResult has no SelectQuery');
+ }
+
+ public function getQuery() {
+ throw new UnimplementedFeatureException('NoSqlResult has no SelectQuery');
+ }
+
+ public function setLimit($limit) {
+ Assert::isInteger($limit);
+ $this->getMongoCursor()->limit($limit);
+ return $this;
+ }
+
+ public function setOffset($offset) {
+ Assert::isInteger($offset);
+ $this->getMongoCursor()->skip($offset);
+ return $this;
+ }
+}
diff --git a/core/NoSQL/NoSqlResultList.class.php b/core/NoSQL/NoSqlResultList.class.php
new file mode 100644
index 0000000000..c2a965301a
--- /dev/null
+++ b/core/NoSQL/NoSqlResultList.class.php
@@ -0,0 +1,92 @@
+
+ * @date 2012.07.02
+ */
+class NoSqlResultList implements Iterator {
+ /** @var NoSqlResult */
+ protected $result;
+
+ /**
+ * @param NoSqlResult $result
+ */
+ protected function __construct(NoSqlResult $result) {
+ $this->result = $result;
+ }
+
+ /**
+ * @param NoSqlResult $result
+ * @return NoSqlResultList
+ */
+ public static function create(NoSqlResult $result) {
+ return new static($result);
+ }
+
+ public function getResult() {
+ return $this->result;
+ }
+
+ public function getCursor() {
+ return $this->getResult()->getMongoCursor();
+ }
+
+ /**
+ * (PHP 5 >= 5.0.0)
+ * Return the current element
+ * @link http://php.net/manual/en/iterator.cur;rent.php
+ * @return mixed Can return any type.
+ */
+ public function current() {
+ $row = $this->getCursor()->current();
+ if ($row['_id'] instanceof MongoId) {
+ $row['id'] = (string)$row['_id'];
+ } else {
+ $row['id'] = $row['_id'];
+ }
+ unset($row['_id']);
+ return $this->result->getDao()->makeNoSqlObject($row);
+ }
+
+ /**
+ * (PHP 5 >= 5.0.0)
+ * Rewind the Iterator to the first element
+ * @link http://php.net/manual/en/iterator.rewind.php
+ * @return void Any returned value is ignored.
+ */
+ public function rewind() {
+ $this->getCursor()->rewind();
+ }
+
+ /**
+ * (PHP 5 >= 5.0.0)
+ * Checks if current position is valid
+ * @link http://php.net/manual/en/iterator.valid.php
+ * @return boolean The return value will be casted to boolean and then evaluated.
+ * Returns true on success or false on failure.
+ */
+ public function valid() {
+ return $this->getCursor()->valid();
+ }
+
+ /**
+ * (PHP 5 >= 5.0.0)
+ * Return the key of the current element
+ * @link http://php.net/manual/en/iterator.key.php
+ * @return scalar scalar on success, or null on failure.
+ */
+ public function key() {
+ return $this->getCursor()->key();
+ }
+
+ /**
+ * (PHP 5 >= 5.0.0)
+ * Move forward to next element
+ * @link http://php.net/manual/en/iterator.next.php
+ * @return void Any returned value is ignored.
+ */
+ public function next() {
+ $this->getCursor()->next();
+ }
+
+}
diff --git a/core/NoSQL/RedisNoSQL.class.php b/core/NoSQL/RedisNoSQL.class.php
new file mode 100644
index 0000000000..c47f5947c2
--- /dev/null
+++ b/core/NoSQL/RedisNoSQL.class.php
@@ -0,0 +1,371 @@
+select($db);
+ return $instance;
+ }
+
+ public function __construct(
+ $host = self::DEFAULT_HOST,
+ $port = self::DEFAULT_PORT,
+ $timeout = self::DEFAULT_TIMEOUT
+ )
+ {
+ $this->host = $host;
+ $this->port = $port;
+ $this->timeout = $timeout;
+ }
+
+ public function __destruct()
+ {
+ if ($this->alive) {
+ try {
+ $this->redis->close(); //if pconnect - it will be ignored
+ } catch (RedisException $e) {
+ // shhhh.
+ }
+ }
+ }
+
+ public function clean()
+ {
+ /** @var Profiling $profiling */
+ $profiling = Profiling::create(array('cache', 'redis'))->begin();
+ $this->ensureTriedToConnect();
+
+ try {
+ $this->redis->flushDB();
+ $profiling
+ ->setInfo('clean')
+ ->end()
+ ;
+ } catch (RedisException $e) {
+ $this->alive = false;
+ }
+
+ return parent::clean();
+ }
+
+ public function isAlive()
+ {
+ $this->ensureTriedToConnect();
+
+ try {
+ $this->alive = $this->redis->ping() == '+PONG';
+ } catch (RedisException $e) {
+ $this->alive = false;
+ }
+
+ return parent::isAlive();
+ }
+
+ public function append($key, $data)
+ {
+ /** @var Profiling $profiling */
+ $profiling = Profiling::create(array('cache', 'redis'))->begin();
+ $this->ensureTriedToConnect();
+
+ try {
+ $response = $this->redis->append($key, $data);
+ $profiling
+ ->setInfo('append ' . $key)
+ ->end()
+ ;
+ return $response;
+ } catch (RedisException $e) {
+ return $this->alive = false;
+ }
+ }
+
+ public function decrement($key, $value)
+ {
+ /** @var Profiling $profiling */
+ $profiling = Profiling::create(array('cache', 'redis'))->begin();
+ $this->ensureTriedToConnect();
+
+ try {
+ $response = $this->redis->decrBy($key, $value);
+ $profiling
+ ->setInfo('decrement ' . $key)
+ ->end()
+ ;
+ return $response;
+ } catch (RedisException $e) {
+ return null;
+ }
+ }
+
+ public function delete($key)
+ {
+ /** @var Profiling $profiling */
+ $profiling = Profiling::create(array('cache', 'redis'))->begin();
+ $this->ensureTriedToConnect();
+
+ try {
+ $response = $this->redis->delete($key);
+ $profiling
+ ->setInfo('delete ' . $key)
+ ->end()
+ ;
+ return $response;
+ } catch (RedisException $e) {
+ return $this->alive = false;
+ }
+ }
+
+ public function get($key)
+ {
+ /** @var Profiling $profiling */
+ $profiling = Profiling::create(array('cache', 'redis'))->begin();
+ $this->ensureTriedToConnect();
+
+ try {
+ $response = $this->redis->get($key);
+ $profiling
+ ->setInfo('get ' . $key)
+ ->end()
+ ;
+ return $response;
+ } catch (RedisException $e) {
+ $this->alive = false;
+
+ return null;
+ }
+ }
+
+ public function keys($mask = null)
+ {
+ /** @var Profiling $profiling */
+ $profiling = Profiling::create(array('cache', 'redis'))->begin();
+ $this->ensureTriedToConnect();
+
+ if (!$mask)
+ $mask = '*';
+
+ try {
+ $response = $this->redis->keys($mask);
+ $profiling
+ ->setInfo('keys ' . $mask)
+ ->end()
+ ;
+ return $response;
+ } catch (RedisException $e) {
+ $this->alive = false;
+
+ return null;
+ }
+ }
+
+ public function increment($key, $value)
+ {
+ /** @var Profiling $profiling */
+ $profiling = Profiling::create(array('cache', 'redis'))->begin();
+ $this->ensureTriedToConnect();
+
+ try {
+ $response = $this->redis->incrBy($key, $value);
+ $profiling
+ ->setInfo('increment ' . $key)
+ ->end()
+ ;
+ return $response;
+ } catch (RedisException $e) {
+ return null;
+ }
+ }
+
+ /**
+ * @param string $key
+ *
+ * @return RedisNoSQLList
+ */
+ public function fetchList($key, $timeout = null)
+ {
+ $this->ensureTriedToConnect();
+
+ return new RedisNoSQLList($this->redis, $key, $timeout);
+ }
+
+ /**
+ * @param string $key
+ *
+ * @return RedisNoSQLSet
+ */
+ public function fetchSet($key)
+ {
+ throw new UnimplementedFeatureException();
+ }
+
+ /**
+ * @param string $key
+ *
+ * @return RedisNoSQLHash
+ */
+ public function fetchHash($key)
+ {
+ throw new UnimplementedFeatureException();
+ }
+
+ public function info() {
+ /** @var Profiling $profiling */
+ $profiling = Profiling::create(array('cache', 'redis'))->begin();
+ $response = $this->redis->info();
+ $profiling
+ ->setInfo('info')
+ ->end()
+ ;
+ return $response;
+ }
+
+ public function select($db) {
+ /** @var Profiling $profiling */
+ $profiling = Profiling::create(array('cache', 'redis'))->begin();
+ $this->ensureTriedToConnect();
+
+ if( is_null($db) || !Assert::checkInteger($db) ) {
+ throw new WrongArgumentException('DB id should be an integer');
+ }
+ $result = $this->redis->select($db);
+ if( !$result ) {
+ throw new WrongStateException('could not change db');
+ }
+ $profiling
+ ->setInfo('select ' . $db)
+ ->end()
+ ;
+ return $result;
+ }
+
+ protected function store($action, $key, $value, $expires = Cache::EXPIRES_MEDIUM)
+ {
+ /** @var Profiling $profiling */
+ $profiling = Profiling::create(array('cache', 'redis'))->begin();
+ $this->ensureTriedToConnect();
+
+ switch ($action) {
+ case 'set':
+ case 'replace':
+ case 'add':
+ try {
+ $result = $this->redis->set($key, $value, $expires);
+ $profiling
+ ->setInfo($action . ' ' . $key)
+ ->end()
+ ;
+ return $result;
+ } catch (RedisException $e) {
+ return $this->alive = false;
+ }
+
+ default:
+ throw new UnimplementedFeatureException();
+ }
+ }
+
+ protected function ensureTriedToConnect()
+ {
+ if ($this->triedConnect)
+ return $this;
+
+ $this->triedConnect = true;
+
+ $this->redis = new Redis();
+
+ try {
+ $this->redis->pconnect($this->host, $this->port, $this->timeout);
+ $this->isAlive();
+ } catch (RedisException $e) {
+ $this->alive = false;
+ }
+
+ return $this;
+ }
+
+ public function deleteList(array $keys) {
+
+ $this->ensureTriedToConnect();
+
+ try {
+ return $this->redis->delete(implode(' ', $keys));
+ } catch (RedisException $e) {
+ return $this->alive = false;
+ }
+ }
+
+ public function deleteByPattern($pattern) {
+
+ $this->ensureTriedToConnect();
+
+ $keys = $this->keys($pattern);
+ if (!$keys)
+ return false;
+
+ try {
+ return $this->redis->delete(implode(' ', $keys));
+ } catch (RedisException $e) {
+ return $this->alive = false;
+ }
+ }
+
+ public function multi() {
+ $this->ensureTriedToConnect();
+ try {
+ return $this->redis->multi();
+ } catch (RedisException $e) {
+ return $this->alive = false;
+ }
+ }
+
+ public function exec() {
+ $this->ensureTriedToConnect();
+ try {
+ return $this->redis->exec();
+ } catch (RedisException $e) {
+ return $this->alive = false;
+ }
+ }
+
+ public function discard() {
+ $this->ensureTriedToConnect();
+ try {
+ return $this->redis->discard();
+ } catch (RedisException $e) {
+ return $this->alive = false;
+ }
+ }
+
+}
diff --git a/core/NoSQL/RedisNoSQLList.class.php b/core/NoSQL/RedisNoSQLList.class.php
new file mode 100644
index 0000000000..d6f2f802a0
--- /dev/null
+++ b/core/NoSQL/RedisNoSQLList.class.php
@@ -0,0 +1,189 @@
+redis = $redis;
+ $this->key = $key;
+ $this->timeout = $timeout;
+ }
+
+ /**
+ * @param mixed $value
+ * @return RedisList
+ */
+ public function append($value)
+ {
+ /** @var Profiling $profiling */
+ $profiling = Profiling::create(array('cache', 'redis'))->begin();
+ $this->redis->rpush($this->key, $value);
+
+ if ($this->timeout)
+ $this->redis->setTimeout($this->key, $this->timeout);
+
+ $profiling->setInfo('rpush ' . $this->key)->end();
+ return $this;
+ }
+
+ /**
+ * @param mixed $value
+ * @return RedisList
+ */
+ public function prepend($value)
+ {
+ /** @var Profiling $profiling */
+ $profiling = Profiling::create(array('cache', 'redis'))->begin();
+ $this->redis->lpush($this->key, $value);
+
+ if ($this->timeout)
+ $this->redis->setTimeout($this->key, $this->timeout);
+
+ $profiling->setInfo('lpush ' . $this->key)->end();
+ return $this;
+ }
+
+ /**
+ * @return RedisList
+ */
+ public function clear()
+ {
+ /** @var Profiling $profiling */
+ $profiling = Profiling::create(array('cache', 'redis'))->begin();
+ $this->redis->LTrim($this->key, -1, 0);
+
+ $profiling->setInfo('LTrim ' . $this->key)->end();
+ return $this;
+ }
+
+
+ public function count()
+ {
+ /** @var Profiling $profiling */
+ $profiling = Profiling::create(array('cache', 'redis'))->begin();
+ $result = $this->redis->lsize($this->key);
+ $profiling->setInfo('lsize ' . $this->key)->end();
+ return $result;
+ }
+
+ public function pop()
+ {
+ /** @var Profiling $profiling */
+ $profiling = Profiling::create(array('cache', 'redis'))->begin();
+ $result = $this->redis->lpop($this->key);
+ $profiling->setInfo('lpop ' . $this->key)->end();
+ return $result;
+ }
+
+ public function range($start, $length = null)
+ {
+ /** @var Profiling $profiling */
+ $profiling = Profiling::create(array('cache', 'redis'))->begin();
+ $end = is_null($length)
+ ? -1
+ : $start + $length;
+
+ $result = $this->redis->lrange($this->key, $start, $end);
+ $profiling->setInfo('lrange ' . $this->key . ' ' . $start . ' ' . $end)->end();
+ return $result;
+ }
+
+ public function get($index)
+ {
+ /** @var Profiling $profiling */
+ $profiling = Profiling::create(array('cache', 'redis'))->begin();
+ $result = $this->redis->lget($this->key, $index);
+ $profiling->setInfo('lget ' . $this->key . ' ' . $index)->end();
+ return $result;
+ }
+
+ public function set($index, $value)
+ {
+ /** @var Profiling $profiling */
+ $profiling = Profiling::create(array('cache', 'redis'))->begin();
+ $this->redis->lset($this->key, $index, $value);
+
+ if ($this->timeout)
+ $this->redis->expire($this->key, $this->timeout);
+
+ $profiling->setInfo('lset ' . $this->key . ' ' . $index)->end();
+ return $this;
+ }
+
+ public function trim($start, $length = null)
+ {
+ $end = is_null($length)
+ ? -1
+ : $start + $length - 1;
+
+ $this->redis->ltrim($this->key, $start, $end);
+ }
+
+ //region Iterator
+ public function current()
+ {
+ return $this->get($this->position);
+ }
+
+ public function key()
+ {
+ return $this->position;
+ }
+
+ public function next()
+ {
+ $this->position++;
+ }
+
+ public function rewind()
+ {
+ $this->position = 0;
+ }
+
+ public function valid()
+ {
+ return $this->offsetExists($this->position);
+ }
+ //endregion
+
+ //region ArrayAccess
+
+ public function offsetExists($offset)
+ {
+ return false !== $this->get($offset);
+ }
+
+ public function offsetGet($offset)
+ {
+ return $this->get($offset);
+ }
+
+ public function offsetSet($offset, $value)
+ {
+ return $this->set($offset, $value);
+ }
+
+ public function offsetUnset($offset)
+ {
+ throw new UnimplementedFeatureException();
+ }
+
+ public function seek($position) {
+ $this->position = $position;
+ }
+ //endregion
+}
\ No newline at end of file
diff --git a/core/OSQL/Castable.class.php b/core/OSQL/Castable.class.php
old mode 100644
new mode 100755
diff --git a/core/OSQL/CombineQuery.class.php b/core/OSQL/CombineQuery.class.php
old mode 100644
new mode 100755
diff --git a/core/OSQL/CreateTableQuery.class.php b/core/OSQL/CreateTableQuery.class.php
old mode 100644
new mode 100755
diff --git a/core/OSQL/DBArray.class.php b/core/OSQL/DBArray.class.php
new file mode 100644
index 0000000000..50851ebd36
--- /dev/null
+++ b/core/OSQL/DBArray.class.php
@@ -0,0 +1,61 @@
+
+ * @date 2013.04.15
+ */
+
+ /**
+ * Container for passing array values into OSQL queries.
+ *
+ * @ingroup OSQL
+ * @ingroup Module
+ **/
+ class DBArray extends DBValue {
+
+ protected $type = null;
+
+ /**
+ * @param int $value
+ * @return DBArray
+ */
+ public static function create($value)
+ {
+ return new self($value);
+ }
+
+ public function integers() {
+ $this->type = DataType::INTEGER;
+ return $this;
+ }
+
+ public function floats() {
+ $this->type = DataType::REAL;
+ return $this;
+ }
+
+ public function strings() {
+ $this->type = DataType::VARCHAR;
+ return $this;
+ }
+
+ public function json() {
+ $this->type = DataType::JSON;
+ return $this;
+ }
+
+ public function jsonb() {
+ $this->type = DataType::JSONB;
+ return $this;
+ }
+
+ public function toDialectString(Dialect $dialect)
+ {
+ if ($this->type == DataType::JSON || $this->type == DataType::JSONB) {
+ return $dialect->quoteJson($this->getValue(), $this->type);
+ } else {
+ return $dialect->quoteArray($this->getValue(), $this->type);
+ }
+ }
+
+ }
\ No newline at end of file
diff --git a/core/OSQL/DBBinary.class.php b/core/OSQL/DBBinary.class.php
old mode 100644
new mode 100755
diff --git a/core/OSQL/DBColumn.class.php b/core/OSQL/DBColumn.class.php
old mode 100644
new mode 100755
index a7a5e9c863..428d7b40be
--- a/core/OSQL/DBColumn.class.php
+++ b/core/OSQL/DBColumn.class.php
@@ -16,18 +16,18 @@ final class DBColumn implements SQLTableName
{
private $type = null;
private $name = null;
-
+
private $table = null;
private $default = null;
-
+
private $reference = null;
private $onUpdate = null;
private $onDelete = null;
-
+
private $primary = null;
-
+
private $sequenced = null;
-
+
/**
* @return DBColumn
**/
@@ -35,13 +35,13 @@ public static function create(DataType $type, $name)
{
return new self($type, $name);
}
-
+
public function __construct(DataType $type, $name)
{
$this->type = $type;
$this->name = $name;
}
-
+
/**
* @return DataType
**/
@@ -49,22 +49,22 @@ public function getType()
{
return $this->type;
}
-
+
/**
* @return DBColumn
**/
public function setTable(DBTable $table)
{
$this->table = $table;
-
+
return $this;
}
-
+
public function getName()
{
return $this->name;
}
-
+
/**
* @return DBTable
**/
@@ -72,37 +72,37 @@ public function getTable()
{
return $this->table;
}
-
+
public function isPrimaryKey()
{
return $this->primary;
}
-
+
/**
* @return DBColumn
**/
public function setPrimaryKey($primary = false)
{
$this->primary = true === $primary;
-
+
return $this;
}
-
+
/**
* @return DBColumn
**/
public function setDefault($default)
{
$this->default = $default;
-
+
return $this;
}
-
+
public function getDefault()
{
return $this->default;
}
-
+
/**
* @throws WrongArgumentException
* @return DBColumn
@@ -123,14 +123,14 @@ public function setReference(
|| $onUpdate instanceof ForeignChangeAction
)
);
-
+
$this->reference = $column;
$this->onDelete = $onDelete;
$this->onUpdate = $onUpdate;
-
+
return $this;
}
-
+
/**
* @return DBColumn
**/
@@ -139,64 +139,64 @@ public function dropReference()
$this->reference = null;
$this->onDelete = null;
$this->onUpdate = null;
-
+
return $this;
}
-
+
public function hasReference()
{
return ($this->reference !== null);
}
-
+
/**
* @return DBColumn
**/
public function setAutoincrement($auto = false)
{
$this->sequenced = (true === $auto);
-
+
return $this;
}
-
+
public function isAutoincrement()
{
return $this->sequenced;
}
-
+
public function toDialectString(Dialect $dialect)
{
$out =
$dialect->quoteField($this->name).' '
.$this->type->toDialectString($dialect);
-
+
if (null !== $this->default) {
-
+
if ($this->type->getId() == DataType::BOOLEAN)
$default = $this->default
? $dialect->literalToString(Dialect::LITERAL_TRUE)
: $dialect->literalToString(Dialect::LITERAL_FALSE);
else
- $default = $dialect->valueToString($default);
-
+ $default = $dialect->valueToString($this->default);
+
$out .= ' DEFAULT '.($default);
}
-
+
if ($this->reference) {
-
+
$table = $this->reference->getTable()->getName();
$column = $this->reference->getName();
-
+
$out .=
" REFERENCES {$dialect->quoteTable($table)}"
."({$dialect->quoteField($column)})";
-
+
if ($this->onDelete)
$out .= ' ON DELETE '.$this->onDelete->toString();
-
+
if ($this->onUpdate)
$out .= ' ON UPDATE '.$this->onUpdate->toString();
}
-
+
return $out;
}
}
diff --git a/core/OSQL/DBField.class.php b/core/OSQL/DBField.class.php
old mode 100644
new mode 100755
index 4c8c26bf8f..be5d93df85
--- a/core/OSQL/DBField.class.php
+++ b/core/OSQL/DBField.class.php
@@ -15,7 +15,7 @@
* @ingroup OSQL
* @ingroup Module
**/
- final class DBField extends Castable implements SQLTableName
+ class DBField extends Castable implements SQLTableName
{
private $field = null;
private $table = null;
diff --git a/core/OSQL/DBHstoreField.class.php b/core/OSQL/DBHstoreField.class.php
new file mode 100644
index 0000000000..38f841a8fe
--- /dev/null
+++ b/core/OSQL/DBHstoreField.class.php
@@ -0,0 +1,65 @@
+setKey($key);
+
+ return $self;
+ }
+
+ public function toDialectString(Dialect $dialect)
+ {
+ $field =
+ (
+ $this->getTable()
+ ? $this->getTable()->toDialectString($dialect).'.'
+ : null
+ )
+ .$dialect->quoteField($this->getField());
+
+ if ($this->key) {
+ $field .= '->\'' . $this->key . '\'';
+ }
+
+ $field = '(' . $field . ')';
+
+ return
+ $this->cast
+ ? $dialect->toCasted($field, $this->cast)
+ : $field;
+ }
+
+ /**
+ * @param string $key
+ * @return $this
+ */
+ public function setKey($key) {
+ $this->key = $key;
+ return $this;
+ }
+}
\ No newline at end of file
diff --git a/core/OSQL/DBJsonField.class.php b/core/OSQL/DBJsonField.class.php
new file mode 100644
index 0000000000..971d3aeecc
--- /dev/null
+++ b/core/OSQL/DBJsonField.class.php
@@ -0,0 +1,65 @@
+setKey($key);
+
+ return $self;
+ }
+
+ public function toDialectString(Dialect $dialect)
+ {
+ $field =
+ (
+ $this->getTable()
+ ? $this->getTable()->toDialectString($dialect).'.'
+ : null
+ )
+ .$dialect->quoteField($this->getField());
+
+ if ($this->key) {
+ $field .= '->\'' . $this->key . '\'';
+ }
+
+ $field = '(' . $field . ')';
+
+ return
+ $this->cast
+ ? $dialect->toCasted($field, $this->cast)
+ : $field;
+ }
+
+ /**
+ * @param string $key
+ * @return $this
+ */
+ public function setKey($key) {
+ $this->key = $key;
+ return $this;
+ }
+}
\ No newline at end of file
diff --git a/core/OSQL/DBRaw.class.php b/core/OSQL/DBRaw.class.php
old mode 100644
new mode 100755
index 3bd7b6c232..12869291bb
--- a/core/OSQL/DBRaw.class.php
+++ b/core/OSQL/DBRaw.class.php
@@ -11,30 +11,31 @@
/**
* Karma's destroyer.
- *
- * @deprecated since the begining of time
- *
+ *
* @ingroup OSQL
**/
final class DBRaw implements LogicalObject
{
private $string = null;
-
+
+ /**
+ * @return DBRaw
+ **/
+ public static function create($value)
+ {
+ return new self($value);
+ }
+
public function __construct($rawString)
{
- if (!defined('__I_HATE_MY_KARMA__'))
- throw new UnsupportedMethodException(
- 'do not use it. please.'
- );
-
$this->string = $rawString;
}
-
+
public function toDialectString(Dialect $dialect)
{
return $this->string;
}
-
+
public function toBoolean(Form $form)
{
throw new UnsupportedMethodException();
diff --git a/core/OSQL/DBSchema.class.php b/core/OSQL/DBSchema.class.php
old mode 100644
new mode 100755
diff --git a/core/OSQL/DBTable.class.php b/core/OSQL/DBTable.class.php
old mode 100644
new mode 100755
index 2f176b034d..079b65079a
--- a/core/OSQL/DBTable.class.php
+++ b/core/OSQL/DBTable.class.php
@@ -139,7 +139,7 @@ public function toDialectString(Dialect $dialect)
{
return OSQL::createTable($this)->toDialectString($dialect);
}
-
+
// TODO: consider port to AlterTable class (unimplemented yet)
public static function findDifferences(
Dialect $dialect,
@@ -150,36 +150,37 @@ public static function findDifferences(
$out = array();
$head = 'ALTER TABLE '.$dialect->quoteTable($target->getName());
-
+
+ /** @var DBColumn[] $sourceColumns */
$sourceColumns = $source->getColumns();
+ /** @var DBColumn[] $targetColumns */
$targetColumns = $target->getColumns();
-
+
foreach ($sourceColumns as $name => $column) {
if (isset($targetColumns[$name])) {
if (
- $column->getType()->getId()
- != $targetColumns[$name]->getType()->getId()
+ ($column->getType()->getId() != $targetColumns[$name]->getType()->getId()
+ || ($column->getType()->getSize() && $column->getType()->getSize() != $targetColumns[$name]->getType()->getSize())
+ || ($column->getType()->getPrecision() && $column->getType()->getPrecision() != $targetColumns[$name]->getType()->getPrecision())
+ )
+ // for vertica: bigint == integer
+ && !($dialect instanceof VerticaDialect && (
+ ($targetColumns[$name]->getType()->getId() == DataType::INTEGER
+ && $column->getType()->getId() == DataType::BIGINT) ||
+ ($targetColumns[$name]->getType()->getId() == DataType::BIGINT
+ && $column->getType()->getId() == DataType::INTEGER)
+ ))
) {
$targetColumn = $targetColumns[$name];
-
+
$out[] =
$head
.' ALTER COLUMN '.$dialect->quoteField($name)
- .' TYPE '.$targetColumn->getType()->toString()
- .(
- $targetColumn->getType()->hasSize()
- ?
- '('
- .$targetColumn->getType()->getSize()
- .(
- $targetColumn->getType()->hasPrecision()
- ? ', '.$targetColumn->getType()->getPrecision()
- : null
- )
- .')'
- : null
- )
- .';';
+ .' TYPE '.$targetColumn->getType()->toTypeDefinition($dialect)
+ . ($targetColumn->getType()->in([ DataType::JSON, DataType::JSONB ])
+ ? ' USING NULL'
+ : '')
+ . "; \t\t -- (has " . $column->getType()->toTypeDefinition($dialect) . ')';
}
if (
@@ -213,7 +214,7 @@ public static function findDifferences(
if ($column->hasReference()) {
$out[] =
- 'CREATE INDEX '.$dialect->quoteField($name.'_idx')
+ 'CREATE INDEX '.$dialect->quoteField($target->getName().'_'.$name.'_idx')
.' ON '.$dialect->quoteTable($target->getName()).
'('.$dialect->quoteField($name).');';
}
diff --git a/core/OSQL/DBValue.class.php b/core/OSQL/DBValue.class.php
old mode 100644
new mode 100755
diff --git a/core/OSQL/DataType.class.php b/core/OSQL/DataType.class.php
old mode 100644
new mode 100755
index b299cb0990..fd0a0b534b
--- a/core/OSQL/DataType.class.php
+++ b/core/OSQL/DataType.class.php
@@ -20,66 +20,89 @@ final class DataType extends Enumeration implements DialectString
const INTEGER = 0x001002;
const BIGINT = 0x001003;
const NUMERIC = 0x001704;
-
+
const REAL = 0x001105;
const DOUBLE = 0x001106;
-
+
const BOOLEAN = 0x000007;
-
+
const CHAR = 0x000108;
const VARCHAR = 0x000109;
const TEXT = 0x00000A;
-
+
const DATE = 0x00000B;
const TIME = 0x000A0C;
const TIMESTAMP = 0x000A0D;
+ const TIMESTAMPTZ = 0x000A0E;
const INTERVAL = 0x00000F;
-
+
const BINARY = 0x00000E;
-
+
const IP = 0x000010;
const IP_RANGE = 0x000011;
-
+ const CIDR = 0x000012;
+
+ const UUID = 0x000005;
+ const HSTORE = 0x000020;
+
+
+ const JSON = 0x000033;
+ const JSONB = 0x000034;
+
+ const SET_OF_STRINGS = 0x010121;
+ const SET_OF_INTEGERS = 0x010022;
+ const SET_OF_FLOATS = 0x010023;
+
const HAVE_SIZE = 0x000100;
const HAVE_PRECISION = 0x000200;
const HAVE_SCALE = 0x000400;
const HAVE_TIMEZONE = 0x000800;
const CAN_BE_UNSIGNED = 0x001000;
-
+ const ARRAY_COLUMN = 0x010000;
+
private $size = null;
private $precision = null;
private $scale = null;
-
+
private $null = true;
private $timezone = false;
private $unsigned = false;
-
+
protected $names = array(
self::SMALLINT => 'SMALLINT',
self::INTEGER => 'INTEGER',
self::BIGINT => 'BIGINT',
self::NUMERIC => 'NUMERIC',
-
+
self::REAL => 'FLOAT',
self::DOUBLE => 'DOUBLE PRECISION',
-
+
self::BOOLEAN => 'BOOLEAN',
-
+
+ self::UUID => 'UUID',
+ self::HSTORE => 'HSTORE',
+
self::CHAR => 'CHARACTER',
self::VARCHAR => 'CHARACTER VARYING',
self::TEXT => 'TEXT',
-
+
self::DATE => 'DATE',
self::TIME => 'TIME',
self::TIMESTAMP => 'TIMESTAMP',
+ self::TIMESTAMPTZ => 'TIMESTAMP',
self::INTERVAL => 'INTERVAL',
-
+
self::BINARY => 'BINARY',
-
+
self::IP => 'IP',
- self::IP_RANGE => 'IP_RANGE'
+ self::IP_RANGE => 'IP_RANGE',
+ self::CIDR => 'CIDR',
+
+ self::SET_OF_STRINGS => 'CHARACTER VARYING',
+ self::SET_OF_INTEGERS => 'INTEGER',
+ self::SET_OF_FLOATS => 'FLOAT',
);
-
+
/**
* @return DataType
**/
@@ -87,17 +110,17 @@ public static function create($id)
{
return new self($id);
}
-
+
public static function getAnyId()
{
return self::BOOLEAN;
}
-
+
public function getSize()
{
return $this->size;
}
-
+
/**
* @throws WrongArgumentException
* @return DataType
@@ -105,23 +128,23 @@ public function getSize()
public function setSize($size)
{
Assert::isInteger($size);
- Assert::isTrue($this->hasSize());
-
+ Assert::isTrue($this->hasSize() || $this->id == self::HSTORE);
+
$this->size = $size;
-
+
return $this;
}
-
+
public function hasSize()
{
return (bool) ($this->id & self::HAVE_SIZE);
}
-
+
public function getPrecision()
{
return $this->precision;
}
-
+
/**
* @throws WrongArgumentException
* @return DataType
@@ -130,22 +153,22 @@ public function setPrecision($precision)
{
Assert::isInteger($precision);
Assert::isTrue(($this->id & self::HAVE_PRECISION) > 0);
-
+
$this->precision = $precision;
-
+
return $this;
}
-
+
public function hasPrecision()
{
return (bool) ($this->id & self::HAVE_PRECISION);
}
-
+
public function getScale()
{
return $this->scale;
}
-
+
/**
* @throws WrongArgumentException
* @return DataType
@@ -154,12 +177,12 @@ public function setScale($scale)
{
Assert::isInteger($scale);
Assert::isTrue(($this->id & self::HAVE_SCALE) > 0);
-
+
$this->scale = $scale;
-
+
return $this;
}
-
+
/**
* @throws WrongArgumentException
* @return DataType
@@ -167,32 +190,32 @@ public function setScale($scale)
public function setTimezoned($zoned = false)
{
Assert::isTrue(($this->id & self::HAVE_TIMEZONE) > 0);
-
+
$this->timezone = (true === $zoned);
-
+
return $this;
}
-
+
public function isTimezoned()
{
return $this->timezone;
}
-
+
/**
* @return DataType
**/
public function setNull($isNull = false)
{
$this->null = ($isNull === true);
-
+
return $this;
}
-
+
public function isNull()
{
return $this->null;
}
-
+
/**
* @throws WrongArgumentException
* @return DataType
@@ -200,46 +223,51 @@ public function isNull()
public function setUnsigned($unsigned = false)
{
Assert::isTrue(($this->id && self::CAN_BE_UNSIGNED) > 0);
-
+
$this->unsigned = ($unsigned === true);
-
+
return $this;
}
-
+
public function isUnsigned()
{
return $this->unsigned;
}
-
- public function toDialectString(Dialect $dialect)
+
+ public function isArrayColumn()
+ {
+ return (bool) ($this->id & self::ARRAY_COLUMN);
+ }
+
+ public function toTypeDefinition(Dialect $dialect)
{
$out = $dialect->typeToString($this);
-
+
if ($this->unsigned) {
$out .= ' UNSIGNED';
}
-
+
if ($this->id & self::HAVE_PRECISION) {
if ($this->precision) {
-
+
switch ($this->id) {
-
+
case self::TIME:
case self::TIMESTAMP:
-
+
$out .= "({$this->precision})";
break;
-
+
case self::NUMERIC:
-
+
$out .=
$this->precision
? "({$this->size}, {$this->precision})"
: "({$this->size})";
break;
-
+
default:
-
+
throw new WrongStateException();
}
}
@@ -248,18 +276,28 @@ public function toDialectString(Dialect $dialect)
throw new WrongStateException(
"type '{$this->name}' must have size"
);
-
+
$out .= "({$this->size})";
}
-
+ if ($this->isArrayColumn()) {
+ $out .= "[]";
+ }
+
if ($this->id & self::HAVE_TIMEZONE)
$out .= $dialect->timeZone($this->timezone);
+ return $out;
+ }
+
+ public function toDialectString(Dialect $dialect)
+ {
+ $out = $this->toTypeDefinition($dialect);
+
$out .=
$this->null
? ' NULL'
: ' NOT NULL';
-
+
return $out;
}
}
diff --git a/core/OSQL/DatePart.class.php b/core/OSQL/DatePart.class.php
old mode 100644
new mode 100755
diff --git a/core/OSQL/DeleteQuery.class.php b/core/OSQL/DeleteQuery.class.php
old mode 100644
new mode 100755
diff --git a/core/OSQL/DialectString.class.php b/core/OSQL/DialectString.class.php
old mode 100644
new mode 100755
diff --git a/core/OSQL/DropTableQuery.class.php b/core/OSQL/DropTableQuery.class.php
old mode 100644
new mode 100755
diff --git a/core/OSQL/ExtractPart.class.php b/core/OSQL/ExtractPart.class.php
old mode 100644
new mode 100755
diff --git a/core/OSQL/FieldGroup.class.php b/core/OSQL/FieldGroup.class.php
old mode 100644
new mode 100755
diff --git a/core/OSQL/FieldTable.class.php b/core/OSQL/FieldTable.class.php
old mode 100644
new mode 100755
diff --git a/core/OSQL/ForeignChangeAction.class.php b/core/OSQL/ForeignChangeAction.class.php
old mode 100644
new mode 100755
diff --git a/core/OSQL/FromTable.class.php b/core/OSQL/FromTable.class.php
old mode 100644
new mode 100755
diff --git a/core/OSQL/FullText.class.php b/core/OSQL/FullText.class.php
old mode 100644
new mode 100755
diff --git a/core/OSQL/FullTextRank.class.php b/core/OSQL/FullTextRank.class.php
old mode 100644
new mode 100755
diff --git a/core/OSQL/FullTextSearch.class.php b/core/OSQL/FullTextSearch.class.php
old mode 100644
new mode 100755
diff --git a/core/OSQL/GroupBy.class.php b/core/OSQL/GroupBy.class.php
old mode 100644
new mode 100755
diff --git a/core/OSQL/InsertOrUpdateQuery.class.php b/core/OSQL/InsertOrUpdateQuery.class.php
old mode 100644
new mode 100755
diff --git a/core/OSQL/InsertQuery.class.php b/core/OSQL/InsertQuery.class.php
old mode 100644
new mode 100755
index e37b2ba217..537b85848f
--- a/core/OSQL/InsertQuery.class.php
+++ b/core/OSQL/InsertQuery.class.php
@@ -60,7 +60,6 @@ public function toDialectString(Dialect $dialect)
}
$query .= parent::toDialectString($dialect);
-
return $query;
}
@@ -83,7 +82,7 @@ protected function toDialectStringValues($query, Dialect $dialect)
{
$fields = array();
$values = array();
-
+
foreach ($this->fields as $var => $val) {
$fields[] = $dialect->quoteField($var);
diff --git a/core/OSQL/JoinCapableQuery.class.php b/core/OSQL/JoinCapableQuery.class.php
old mode 100644
new mode 100755
diff --git a/core/OSQL/Joiner.class.php b/core/OSQL/Joiner.class.php
old mode 100644
new mode 100755
diff --git a/core/OSQL/OSQL.class.php b/core/OSQL/OSQL.class.php
old mode 100644
new mode 100755
diff --git a/core/OSQL/OrderBy.class.php b/core/OSQL/OrderBy.class.php
old mode 100644
new mode 100755
diff --git a/core/OSQL/OrderChain.class.php b/core/OSQL/OrderChain.class.php
old mode 100644
new mode 100755
index 5eab7e2ee7..eeb9c58945
--- a/core/OSQL/OrderChain.class.php
+++ b/core/OSQL/OrderChain.class.php
@@ -54,11 +54,24 @@ public function getLast()
{
return end($this->chain);
}
+
+ /**
+ * @return OrderBy
+ **/
+ public function getFirst()
+ {
+ return reset($this->chain);
+ }
public function getList()
{
return $this->chain;
}
+
+ public function dropChain()
+ {
+ $this->chain = array();
+ }
public function getCount()
{
@@ -71,10 +84,26 @@ public function getCount()
public function toMapped(ProtoDAO $dao, JoinCapableQuery $query)
{
$chain = new self;
-
- foreach ($this->chain as $order)
- $chain->add($order->toMapped($dao, $query));
-
+
+ foreach ($this->chain as $order) {
+ /**
+ * если используется сортировка по ключ hstore,
+ * то необходимо добавить такое же выражение в список выбираемых столбцов,
+ * иначе получим ошибку от postgres
+ */
+ if (
+ $query->isDistinct()
+ && $dao->isTranslatedField($order->getField())
+ ) {
+ $query->get(DBHstoreField::create(
+ $dao->getProtoClass()->getPropertyByName($order->getField())->getColumnName(),
+ $dao->getTable(),
+ $dao->getLanguageCode()
+ ));
+ }
+ $chain->add($order->toMapped($dao, $query));
+ }
+
return $chain;
}
diff --git a/core/OSQL/Query.class.php b/core/OSQL/Query.class.php
old mode 100644
new mode 100755
diff --git a/core/OSQL/QueryChain.class.php b/core/OSQL/QueryChain.class.php
old mode 100644
new mode 100755
diff --git a/core/OSQL/QueryCombination.class.php b/core/OSQL/QueryCombination.class.php
old mode 100644
new mode 100755
diff --git a/core/OSQL/QueryIdentification.class.php b/core/OSQL/QueryIdentification.class.php
old mode 100644
new mode 100755
diff --git a/core/OSQL/QueryResult.class.php b/core/OSQL/QueryResult.class.php
old mode 100644
new mode 100755
index dfd94d6b17..ad8d7d77cf
--- a/core/OSQL/QueryResult.class.php
+++ b/core/OSQL/QueryResult.class.php
@@ -14,7 +14,7 @@
*
* @ingroup OSQL
**/
- final class QueryResult implements Identifiable
+ class QueryResult implements Identifiable
{
private $list = array();
diff --git a/core/OSQL/QuerySkeleton.class.php b/core/OSQL/QuerySkeleton.class.php
old mode 100644
new mode 100755
diff --git a/core/OSQL/SQLArray.class.php b/core/OSQL/SQLArray.class.php
old mode 100644
new mode 100755
diff --git a/core/OSQL/SQLBaseJoin.class.php b/core/OSQL/SQLBaseJoin.class.php
old mode 100644
new mode 100755
index 00468053b6..a98916e420
--- a/core/OSQL/SQLBaseJoin.class.php
+++ b/core/OSQL/SQLBaseJoin.class.php
@@ -24,6 +24,10 @@ public function __construct($subject, LogicalObject $logic, $alias)
$this->alias = $alias;
$this->logic = $logic;
}
+
+ public static function create($subject, LogicalObject $logic, $alias) {
+ return new static($subject, $logic, $alias);
+ }
public function getAlias()
{
diff --git a/core/OSQL/SQLChain.class.php b/core/OSQL/SQLChain.class.php
old mode 100644
new mode 100755
index 6d08b88e0c..db1242ec52
--- a/core/OSQL/SQLChain.class.php
+++ b/core/OSQL/SQLChain.class.php
@@ -37,7 +37,23 @@ public function getChain()
{
return $this->chain;
}
-
+
+ /**
+ * @param array $chain
+ * @return SQLChain
+ */
+ public function setChain($chain)
+ {
+ $this->chain = $chain;
+ return $this;
+ }
+
+ public function dropChain()
+ {
+ $this->chain = array();
+ $this->logic = array();
+ }
+
public function getLogic()
{
return $this->logic;
diff --git a/core/OSQL/SQLFunction.class.php b/core/OSQL/SQLFunction.class.php
old mode 100644
new mode 100755
diff --git a/core/OSQL/SQLJoin.class.php b/core/OSQL/SQLJoin.class.php
old mode 100644
new mode 100755
diff --git a/core/OSQL/SQLLeftJoin.class.php b/core/OSQL/SQLLeftJoin.class.php
old mode 100644
new mode 100755
diff --git a/core/OSQL/SQLRightJoin.class.php b/core/OSQL/SQLRightJoin.class.php
old mode 100644
new mode 100755
diff --git a/core/OSQL/SQLStringConcat.class.php b/core/OSQL/SQLStringConcat.class.php
new file mode 100644
index 0000000000..28cb01c60c
--- /dev/null
+++ b/core/OSQL/SQLStringConcat.class.php
@@ -0,0 +1,107 @@
+
+ * @date 21.07.2014
+ */
+
+/**
+ * @ingroup OSQL
+ **/
+class SQLStringConcat extends Castable implements MappableObject, Aliased
+{
+ const OPERATOR = '||';
+
+ protected $args = array();
+ protected $alias = null;
+
+ /**
+ * @param $arg1
+ * @param $arg2
+ * @param ...
+ * @param $argN
+ * @return self
+ */
+ public static function create() {
+ $self = new self;
+ $self->args = func_get_args();
+ return $self;
+ }
+
+ protected function __construct() {}
+
+ /**
+ * @param string $alias
+ * @return self
+ */
+ public function setAlias($alias) {
+ $this->alias = $alias;
+ return $this;
+ }
+
+ /**
+ * @return string|null
+ */
+ public function getAlias() {
+ return $this->alias;
+ }
+
+ /**
+ * @param $arg
+ * @return self
+ */
+ public function add($arg) {
+ $this->args []= $arg;
+ return $this;
+ }
+
+ /**
+ * @param ProtoDAO $dao
+ * @param JoinCapableQuery $query
+ * @return self
+ */
+ public function toMapped(ProtoDAO $dao, JoinCapableQuery $query) {
+ $mapped = self::create();
+
+ foreach ($this->args as $arg) {
+ if ($arg instanceof MappableObject) {
+ $mapped->add($arg->toMapped($dao, $query));
+ } else {
+ $mapped->add($dao->guessAtom($arg, $query));
+ }
+ }
+
+ if ($this->alias) {
+ $mapped->setAlias($this->alias);
+ }
+
+ if ($this->cast) {
+ $mapped->castTo($this->cast);
+ }
+
+ return $mapped;
+ }
+
+ public function toDialectString(Dialect $dialect) {
+ $strings = array();
+ foreach ($this->args as $arg) {
+ $strings []= $dialect->toValueString($arg);
+ }
+
+ $sql = '('
+ . implode(
+ ' ' . $dialect->logicToString(self::OPERATOR) . ' ',
+ $strings
+ )
+ . ')';
+
+ if ($this->alias) {
+ $sql .= ' AS ' . $dialect->quoteTable($this->alias);
+ }
+
+ if ($this->cast) {
+ $sql = $dialect->toCasted($sql, $this->cast);
+ }
+
+ return $sql;
+ }
+}
diff --git a/core/OSQL/SQLTableName.class.php b/core/OSQL/SQLTableName.class.php
old mode 100644
new mode 100755
diff --git a/core/OSQL/SQLTimezoneConvert.class.php b/core/OSQL/SQLTimezoneConvert.class.php
new file mode 100644
index 0000000000..2150a95130
--- /dev/null
+++ b/core/OSQL/SQLTimezoneConvert.class.php
@@ -0,0 +1,88 @@
+
+ * @date 11.10.13
+ */
+
+/**
+ * @ingroup OSQL
+ **/
+final class SQLTimezoneConvert extends Castable implements MappableObject, Aliased
+{
+ private $date = null;
+ private $alias = null;
+ private $timezone = null;
+
+ /**
+ * @param $date
+ * @param $timezone
+ * @return self
+ */
+ public static function create($date, $timezone) {
+ return new self($date, $timezone);
+ }
+
+ public function __construct($date, $timezone) {
+ $this->date = $date;
+ $this->timezone = $timezone;
+ }
+
+ /**
+ * @param $alias
+ * @return self
+ */
+ public function setAlias($alias) {
+ $this->alias = $alias;
+ return $this;
+ }
+
+ public function getAlias() {
+ return $this->alias;
+ }
+
+ public function getDate() {
+ return $this->date;
+ }
+
+ /**
+ * @param ProtoDAO $dao
+ * @param JoinCapableQuery $query
+ * @return self
+ */
+ public function toMapped(ProtoDAO $dao, JoinCapableQuery $query) {
+ if ($this->date instanceof MappableObject) {
+ $date = $this->date->toMapped($dao, $query);
+ } else {
+ $date = $dao->guessAtom($this->date, $query);
+ }
+
+ $mapped = self::create($date, $this->timezone);
+ if ($this->cast) {
+ $mapped->castTo($this->cast);
+ }
+ if ($this->alias) {
+ $mapped->setAlias($this->alias);
+ }
+
+ return $mapped;
+ }
+
+ public function toDialectString(Dialect $dialect) {
+ $sql = '('
+ . $dialect->fieldToString($this->getDate())
+ . ' AT TIME ZONE '
+ . $dialect->valueToString($this->timezone)
+ . ')';
+
+ if ($this->cast) {
+ $sql = $dialect->toCasted($sql, $this->cast);
+ }
+ if ($this->alias) {
+ $sql .= ' AS ' . $dialect->quoteTable($this->alias);
+ }
+
+ return $sql;
+ }
+}
diff --git a/core/OSQL/SelectField.class.php b/core/OSQL/SelectField.class.php
old mode 100644
new mode 100755
diff --git a/core/OSQL/SelectQuery.class.php b/core/OSQL/SelectQuery.class.php
old mode 100644
new mode 100755
index 7136be8387..dfe8747546
--- a/core/OSQL/SelectQuery.class.php
+++ b/core/OSQL/SelectQuery.class.php
@@ -32,34 +32,36 @@ final class SelectQuery
private $group = array();
private $having = null;
-
+
+ private $criteria = null;
+
public function __construct()
{
$this->joiner = new Joiner();
$this->order = new OrderChain();
}
-
+
public function __clone()
{
$this->joiner = clone $this->joiner;
$this->order = clone $this->order;
}
-
+
public function hasAliasInside($alias)
{
return isset($this->aliases[$alias]);
}
-
+
public function getAlias()
{
return $this->name;
}
-
+
public function getName()
{
return $this->name;
}
-
+
/**
* @return SelectQuery
**/
@@ -67,10 +69,10 @@ public function setName($name)
{
$this->name = $name;
$this->aliases[$name] = true;
-
+
return $this;
}
-
+
/**
* @return SelectQuery
**/
@@ -79,12 +81,12 @@ public function distinct()
$this->distinct = true;
return $this;
}
-
+
public function isDistinct()
{
return $this->distinct;
}
-
+
/**
* @return SelectQuery
**/
@@ -93,12 +95,32 @@ public function unDistinct()
$this->distinct = false;
return $this;
}
-
+
public function hasJoinedTable($table)
{
return $this->joiner->hasJoinedTable($table);
}
-
+
+ /**
+ * @param SQLBaseJoin $join
+ * @return $this
+ */
+ public function customJoin(SQLBaseJoin $join) {
+ if ($join instanceof SQLLeftJoin) {
+ $this->joiner->leftJoin($join);
+ } else if ($join instanceof SQLRightJoin) {
+ $this->joiner->rightJoin($join);
+ } else if ($join instanceof SQLJoin){
+ $this->joiner->join($join);
+ } else {
+ throw new UnexpectedValueException(var_export($join, true));
+ }
+
+ $this->aliases[$join->getAlias()] = true;
+
+ return $this;
+ }
+
/**
* @return SelectQuery
**/
@@ -106,10 +128,10 @@ public function join($table, LogicalObject $logic, $alias = null)
{
$this->joiner->join(new SQLJoin($table, $logic, $alias));
$this->aliases[$alias] = true;
-
+
return $this;
}
-
+
/**
* @return SelectQuery
**/
@@ -389,7 +411,7 @@ public function toDialectString(Dialect $dialect)
'SELECT '.($this->distinct ? 'DISTINCT ' : null)
.implode(', ', $fieldList)
.$this->joiner->toDialectString($dialect);
-
+
// WHERE
$query .= parent::toDialectString($dialect);
@@ -405,17 +427,17 @@ public function toDialectString(Dialect $dialect)
if ($this->having)
$query .= ' HAVING '.$this->having->toDialectString($dialect);
-
+
if ($this->order->getCount()) {
$query .= ' ORDER BY '.$this->order->toDialectString($dialect);
}
-
+
if ($this->limit)
$query .= ' LIMIT '.$this->limit;
-
+
if ($this->offset)
$query .= ' OFFSET '.$this->offset;
-
+
return $query;
}
@@ -470,5 +492,19 @@ private function makeOrder($field, $table = null)
new DBField($field, $this->getLastTable($table))
);
}
+
+ /**
+ * @param $criteria
+ */
+ public function setCriteria($criteria) {
+ $this->criteria = $criteria;
+ }
+
+ /*
+ * @return Criteria|null
+ */
+ public function getCriteria() {
+ return $this->criteria;
+ }
}
?>
\ No newline at end of file
diff --git a/core/OSQL/TimeIntervalsGenerator.class.php b/core/OSQL/TimeIntervalsGenerator.class.php
old mode 100644
new mode 100755
diff --git a/core/OSQL/TruncateQuery.class.php b/core/OSQL/TruncateQuery.class.php
old mode 100644
new mode 100755
diff --git a/core/OSQL/UpdateQuery.class.php b/core/OSQL/UpdateQuery.class.php
old mode 100644
new mode 100755
diff --git a/doc/AUTHORS b/doc/AUTHORS
old mode 100644
new mode 100755
diff --git a/doc/ChangeLog b/doc/ChangeLog
old mode 100644
new mode 100755
diff --git a/doc/CodingStyle b/doc/CodingStyle
old mode 100644
new mode 100755
diff --git a/doc/Doxyfile b/doc/Doxyfile
old mode 100644
new mode 100755
diff --git a/doc/FAQ b/doc/FAQ
old mode 100644
new mode 100755
diff --git a/doc/FEATURES b/doc/FEATURES
old mode 100644
new mode 100755
diff --git a/doc/LICENSE b/doc/LICENSE
old mode 100644
new mode 100755
diff --git a/doc/OQL-BNF b/doc/OQL-BNF
old mode 100644
new mode 100755
diff --git a/doc/README b/doc/README
old mode 100644
new mode 100755
diff --git a/doc/ROUTERS b/doc/ROUTERS
old mode 100644
new mode 100755
diff --git a/doc/THANKS b/doc/THANKS
old mode 100644
new mode 100755
diff --git a/doc/TODO b/doc/TODO
old mode 100644
new mode 100755
diff --git a/doc/examples/cacheSettings.php b/doc/examples/cacheSettings.php
old mode 100644
new mode 100755
diff --git a/doc/examples/index.php b/doc/examples/index.php
old mode 100644
new mode 100755
diff --git a/doc/examples/singleton.php b/doc/examples/singleton.php
old mode 100644
new mode 100755
diff --git a/doc/examples/union.php b/doc/examples/union.php
old mode 100644
new mode 100755
diff --git a/doc/onphp.doxy.php b/doc/onphp.doxy.php
old mode 100644
new mode 100755
diff --git a/doc/parts/doxyHead.html b/doc/parts/doxyHead.html
old mode 100644
new mode 100755
diff --git a/doc/parts/doxyHeel.html b/doc/parts/doxyHeel.html
old mode 100644
new mode 100755
diff --git a/doc/patches/core_DB_Dialect.diff b/doc/patches/core_DB_Dialect.diff
new file mode 100755
index 0000000000..a1b3fc6c36
--- /dev/null
+++ b/doc/patches/core_DB_Dialect.diff
@@ -0,0 +1,10 @@
+@@ -85,6 +85,9 @@
+ if ($type->getId() == DataType::IP_RANGE)
+ return 'varchar(41)';
+
++ if ($type->getId() == DataType::UUID)
++ return 'varchar(36)';
++
+ return $type->getName();
+ }
+
diff --git a/doc/patches/global-inc.diff b/doc/patches/global-inc.diff
new file mode 100755
index 0000000000..b5ed717817
--- /dev/null
+++ b/doc/patches/global-inc.diff
@@ -0,0 +1,9 @@
+@@ -103,6 +103,8 @@
+ .ONPHP_CORE_PATH.'Logic' .PATH_SEPARATOR
+ .ONPHP_CORE_PATH.'OSQL' .PATH_SEPARATOR
+
++ .ONPHP_CORE_PATH.'NoSQL' .PATH_SEPARATOR
++
+ // main framework
+ .ONPHP_MAIN_PATH.'Base' .PATH_SEPARATOR
+
diff --git a/doc/project.skel/config.inc.php.tpl b/doc/project.skel/config.inc.php.tpl
old mode 100644
new mode 100755
diff --git a/doc/project.skel/config.xml b/doc/project.skel/config.xml
old mode 100644
new mode 100755
diff --git a/doc/project.skel/db/schema.sql b/doc/project.skel/db/schema.sql
old mode 100644
new mode 100755
diff --git a/doc/project.skel/src/admin/controllers/login.class.php b/doc/project.skel/src/admin/controllers/login.class.php
old mode 100644
new mode 100755
diff --git a/doc/project.skel/src/admin/controllers/main.class.php b/doc/project.skel/src/admin/controllers/main.class.php
old mode 100644
new mode 100755
diff --git a/doc/project.skel/src/admin/htdocs/index.php b/doc/project.skel/src/admin/htdocs/index.php
old mode 100644
new mode 100755
diff --git a/doc/project.skel/src/admin/templates/login.tpl.html b/doc/project.skel/src/admin/templates/login.tpl.html
old mode 100644
new mode 100755
diff --git a/doc/project.skel/src/admin/templates/main.tpl.html b/doc/project.skel/src/admin/templates/main.tpl.html
old mode 100644
new mode 100755
diff --git a/doc/project.skel/src/classes/Auto/Business/AutoAdministrator.class.php b/doc/project.skel/src/classes/Auto/Business/AutoAdministrator.class.php
old mode 100644
new mode 100755
diff --git a/doc/project.skel/src/classes/Auto/DAOs/AutoAdministratorDAO.class.php b/doc/project.skel/src/classes/Auto/DAOs/AutoAdministratorDAO.class.php
old mode 100644
new mode 100755
diff --git a/doc/project.skel/src/classes/Auto/Proto/AutoProtoAdministrator.class.php b/doc/project.skel/src/classes/Auto/Proto/AutoProtoAdministrator.class.php
old mode 100644
new mode 100755
diff --git a/doc/project.skel/src/classes/Auto/schema.php b/doc/project.skel/src/classes/Auto/schema.php
old mode 100644
new mode 100755
diff --git a/doc/project.skel/src/classes/Business/Administrator.class.php b/doc/project.skel/src/classes/Business/Administrator.class.php
old mode 100644
new mode 100755
diff --git a/doc/project.skel/src/classes/DAOs/AdministratorDAO.class.php b/doc/project.skel/src/classes/DAOs/AdministratorDAO.class.php
old mode 100644
new mode 100755
diff --git a/doc/project.skel/src/classes/Flow/AuthorizationFilter.class.php b/doc/project.skel/src/classes/Flow/AuthorizationFilter.class.php
old mode 100644
new mode 100755
diff --git a/doc/project.skel/src/classes/Proto/ProtoAdministrator.class.php b/doc/project.skel/src/classes/Proto/ProtoAdministrator.class.php
old mode 100644
new mode 100755
diff --git a/doc/project.skel/src/user/controllers/main.class.php b/doc/project.skel/src/user/controllers/main.class.php
old mode 100644
new mode 100755
diff --git a/doc/project.skel/src/user/htdocs/index.php b/doc/project.skel/src/user/htdocs/index.php
old mode 100644
new mode 100755
diff --git a/doc/project.skel/src/user/templates/main.tpl.html b/doc/project.skel/src/user/templates/main.tpl.html
old mode 100644
new mode 100755
diff --git a/global.inc.php.tpl b/global.inc.php.tpl
old mode 100644
new mode 100755
index 7ad77d44a9..34061e533e
--- a/global.inc.php.tpl
+++ b/global.inc.php.tpl
@@ -10,12 +10,12 @@
***************************************************************************/
// sample system-wide configuration file
-
+
function error2Exception($code, $string, $file, $line, $context)
{
throw new BaseException($string, $code);
}
-
+
/* void */ function __autoload_failed($classname, $message)
{
eval(
@@ -24,14 +24,14 @@
.'throw new ClassNotFoundException("'.$classname.': '.$message.'");'
);
}
-
+
// file extensions
define('EXT_CLASS', '.class.php');
define('EXT_TPL', '.tpl.html');
define('EXT_MOD', '.inc.php');
define('EXT_HTML', '.html');
define('EXT_UNIT', '.unit.php');
-
+
// overridable constant, don't forget for trailing slash
// also you may consider using /dev/shm/ for cache purposes
if (!defined('ONPHP_TEMP_PATH'))
@@ -39,113 +39,117 @@
'ONPHP_TEMP_PATH',
sys_get_temp_dir().DIRECTORY_SEPARATOR.'onPHP'.DIRECTORY_SEPARATOR
);
-
+
if (!defined('ONPHP_CLASS_CACHE'))
define('ONPHP_CLASS_CACHE', ONPHP_TEMP_PATH);
-
+
// classes autoload magic
if (!defined('ONPHP_CLASS_CACHE_TYPE'))
define('ONPHP_CLASS_CACHE_TYPE', 'classPathCache');
-
+
require
dirname(__FILE__).DIRECTORY_SEPARATOR
.'misc'.DIRECTORY_SEPARATOR
.'Autoloader'.EXT_MOD;
-
+
spl_autoload_register(array('Autoloader', ONPHP_CLASS_CACHE_TYPE), false);
-
+
// system settings
error_reporting(E_ALL | E_STRICT);
set_error_handler('error2Exception', E_ALL | E_STRICT);
ignore_user_abort(true);
define('ONPHP_VERSION', '1.0.10.99');
-
+
if (!defined('ONPHP_IPC_PERMS'))
define('ONPHP_IPC_PERMS', 0660);
-
+
// paths
define('ONPHP_ROOT_PATH', dirname(__FILE__).DIRECTORY_SEPARATOR);
define('ONPHP_CORE_PATH', ONPHP_ROOT_PATH.'core'.DIRECTORY_SEPARATOR);
define('ONPHP_MAIN_PATH', ONPHP_ROOT_PATH.'main'.DIRECTORY_SEPARATOR);
define('ONPHP_META_PATH', ONPHP_ROOT_PATH.'meta'.DIRECTORY_SEPARATOR);
define('ONPHP_UI_PATH', ONPHP_ROOT_PATH.'UI'.DIRECTORY_SEPARATOR);
-
+ define('ONPHP_LIB_PATH', ONPHP_ROOT_PATH.'lib'.DIRECTORY_SEPARATOR);
+
if (!defined('ONPHP_META_PATH'))
define(
'ONPHP_META_PATH',
ONPHP_ROOT_PATH.'meta'.DIRECTORY_SEPARATOR
);
-
+
define('ONPHP_META_CLASSES', ONPHP_META_PATH.'classes'.DIRECTORY_SEPARATOR);
-
+
define(
'ONPHP_INCUBATOR_PATH',
ONPHP_ROOT_PATH.'incubator'.DIRECTORY_SEPARATOR
);
-
+
set_include_path(
// current path
get_include_path().PATH_SEPARATOR
-
+
// core classes
.ONPHP_CORE_PATH.'Base' .PATH_SEPARATOR
.ONPHP_CORE_PATH.'Cache' .PATH_SEPARATOR
-
+
.ONPHP_CORE_PATH.'DB' .PATH_SEPARATOR
.ONPHP_CORE_PATH.'DB'.DIRECTORY_SEPARATOR.'Transaction'.PATH_SEPARATOR
-
+
.ONPHP_CORE_PATH.'Exceptions' .PATH_SEPARATOR
-
+
.ONPHP_CORE_PATH.'Form' .PATH_SEPARATOR
.ONPHP_CORE_PATH.'Form'.DIRECTORY_SEPARATOR.'Filters'.PATH_SEPARATOR
.ONPHP_CORE_PATH.'Form'.DIRECTORY_SEPARATOR.'Primitives'.PATH_SEPARATOR
-
+
.ONPHP_CORE_PATH.'Logic' .PATH_SEPARATOR
.ONPHP_CORE_PATH.'OSQL' .PATH_SEPARATOR
-
+
+ .ONPHP_CORE_PATH.'NoSQL' .PATH_SEPARATOR
+
// main framework
.ONPHP_MAIN_PATH.'Base' .PATH_SEPARATOR
-
+
.ONPHP_MAIN_PATH.'Criteria' .PATH_SEPARATOR
.ONPHP_MAIN_PATH.'Criteria'.DIRECTORY_SEPARATOR.'Projections'.PATH_SEPARATOR
-
+
.ONPHP_MAIN_PATH.'Crypto' .PATH_SEPARATOR
-
+
.ONPHP_MAIN_PATH.'DAOs' .PATH_SEPARATOR
.ONPHP_MAIN_PATH.'DAOs'.DIRECTORY_SEPARATOR.'Handlers'.PATH_SEPARATOR
.ONPHP_MAIN_PATH.'DAOs'.DIRECTORY_SEPARATOR.'Workers'.PATH_SEPARATOR
-
+
.ONPHP_MAIN_PATH.'Flow' .PATH_SEPARATOR
.ONPHP_MAIN_PATH.'SPL' .PATH_SEPARATOR
-
+
.ONPHP_MAIN_PATH.'Net' .PATH_SEPARATOR
.ONPHP_MAIN_PATH.'Net'.DIRECTORY_SEPARATOR.'Http'.PATH_SEPARATOR
.ONPHP_MAIN_PATH.'Net'.DIRECTORY_SEPARATOR.'Mail'.PATH_SEPARATOR
.ONPHP_MAIN_PATH.'Net'.DIRECTORY_SEPARATOR.'Ip'.PATH_SEPARATOR
.ONPHP_MAIN_PATH.'Net'.DIRECTORY_SEPARATOR.'Soap'.PATH_SEPARATOR
-
+
.ONPHP_MAIN_PATH.'Math' .PATH_SEPARATOR
-
+
.ONPHP_MAIN_PATH.'Markup' .PATH_SEPARATOR
.ONPHP_MAIN_PATH.'Markup'.DIRECTORY_SEPARATOR.'Feed'.PATH_SEPARATOR
.ONPHP_MAIN_PATH.'Markup'.DIRECTORY_SEPARATOR.'Html'.PATH_SEPARATOR
-
+
.ONPHP_MAIN_PATH.'OQL' .PATH_SEPARATOR
.ONPHP_MAIN_PATH.'OQL'.DIRECTORY_SEPARATOR.'Expressions'.PATH_SEPARATOR
.ONPHP_MAIN_PATH.'OQL'.DIRECTORY_SEPARATOR.'Parsers'.PATH_SEPARATOR
.ONPHP_MAIN_PATH.'OQL'.DIRECTORY_SEPARATOR.'Statements'.PATH_SEPARATOR
-
+
.ONPHP_MAIN_PATH.'OpenId' .PATH_SEPARATOR
-
+
.ONPHP_MAIN_PATH.'EntityProto'.PATH_SEPARATOR
.ONPHP_MAIN_PATH.'EntityProto'.DIRECTORY_SEPARATOR.'Builders'.PATH_SEPARATOR
.ONPHP_MAIN_PATH.'EntityProto'.DIRECTORY_SEPARATOR.'Accessors'.PATH_SEPARATOR
-
+
.ONPHP_MAIN_PATH.'UnifiedContainer'.PATH_SEPARATOR
.ONPHP_MAIN_PATH.'UI'.PATH_SEPARATOR
.ONPHP_MAIN_PATH.'UI'.DIRECTORY_SEPARATOR.'View'.PATH_SEPARATOR
-
+ .ONPHP_MAIN_PATH.'UI'.DIRECTORY_SEPARATOR.'Widget'.PATH_SEPARATOR
+
.ONPHP_MAIN_PATH.'Utils' .PATH_SEPARATOR
.ONPHP_MAIN_PATH.'Utils'.DIRECTORY_SEPARATOR.'TuringTest'.PATH_SEPARATOR
.ONPHP_MAIN_PATH.'Utils'.DIRECTORY_SEPARATOR.'Archivers'.PATH_SEPARATOR
@@ -154,6 +158,7 @@
.ONPHP_MAIN_PATH.'Utils'.DIRECTORY_SEPARATOR.'Mobile'.PATH_SEPARATOR
.ONPHP_MAIN_PATH.'Utils'.DIRECTORY_SEPARATOR.'CommandLine'.PATH_SEPARATOR
.ONPHP_MAIN_PATH.'Utils'.DIRECTORY_SEPARATOR.'Routers'.PATH_SEPARATOR
+ .ONPHP_MAIN_PATH.'Utils'.DIRECTORY_SEPARATOR.'Translation'.PATH_SEPARATOR
.ONPHP_MAIN_PATH.'Utils'.DIRECTORY_SEPARATOR.'AMQP'.PATH_SEPARATOR
.ONPHP_MAIN_PATH.'Utils'.DIRECTORY_SEPARATOR.'AMQP'
@@ -165,23 +170,23 @@
.ONPHP_MAIN_PATH.'Messages'.DIRECTORY_SEPARATOR.'Interface'.PATH_SEPARATOR
.ONPHP_MAIN_PATH.'Application' .PATH_SEPARATOR
-
+
.ONPHP_MAIN_PATH.'Charts'.PATH_SEPARATOR
.ONPHP_MAIN_PATH.'Charts'.DIRECTORY_SEPARATOR.'Google'.PATH_SEPARATOR
.ONPHP_MAIN_PATH.'Monitoring'.PATH_SEPARATOR
-
+
.ONPHP_META_CLASSES.PATH_SEPARATOR
-
+
/*
.ONPHP_INCUBATOR_PATH
.'classes'.DIRECTORY_SEPARATOR
.'Application'.DIRECTORY_SEPARATOR.PATH_SEPARATOR
-
+
.ONPHP_INCUBATOR_PATH
.'classes'.DIRECTORY_SEPARATOR
.'Application'.DIRECTORY_SEPARATOR
.'Markups'.DIRECTORY_SEPARATOR.PATH_SEPARATOR
-
+
.ONPHP_INCUBATOR_PATH
.'classes'.DIRECTORY_SEPARATOR
.'Application'.DIRECTORY_SEPARATOR
@@ -189,7 +194,7 @@
.'Documents'.DIRECTORY_SEPARATOR.PATH_SEPARATOR
*/
);
-
+
//NOTE: disable by default
//see http://pgfoundry.org/docman/view.php/1000079/117/README.txt
//define('POSTGRES_IP4_ENABLED', true);
diff --git a/main/Application/ApplicationUrl.class.php b/main/Application/ApplicationUrl.class.php
old mode 100644
new mode 100755
diff --git a/main/Application/ScopeNavigationSchema.class.php b/main/Application/ScopeNavigationSchema.class.php
old mode 100644
new mode 100755
diff --git a/main/Application/SimpleApplicationUrl.class.php b/main/Application/SimpleApplicationUrl.class.php
old mode 100644
new mode 100755
diff --git a/main/Base/AbstractCollection.class.php b/main/Base/AbstractCollection.class.php
old mode 100644
new mode 100755
diff --git a/main/Base/AbstractProtoClass.class.php b/main/Base/AbstractProtoClass.class.php
old mode 100644
new mode 100755
index ebd9169266..1307c40e5d
--- a/main/Base/AbstractProtoClass.class.php
+++ b/main/Base/AbstractProtoClass.class.php
@@ -17,9 +17,9 @@ abstract class AbstractProtoClass extends Singleton
private $depth = 0;
private $storage = array();
private $skipList = array();
-
+
abstract protected function makePropertyList();
-
+
/**
* @return AbstractProtoClass
**/
@@ -27,10 +27,10 @@ public function beginPrefetch()
{
$this->storage[++$this->depth] = array();
$this->skipList[$this->depth] = array();
-
+
return $this;
}
-
+
/**
* @return AbstractProtoClass
**/
@@ -42,45 +42,45 @@ public function skipObjectPrefetching(Identifiable $object)
else
++$this->skipList[$this->depth][$object->getId()];
}
-
+
return $this;
}
-
+
public function endPrefetch(array $objectList)
{
if (!$this->depth)
throw new WrongStateException('prefetch mode is already off');
-
+
foreach ($this->storage[$this->depth] as $setter => $innerList) {
Assert::isEqual(
count($objectList),
count($innerList) + array_sum($this->skipList[$this->depth])
);
-
+
$ids = array();
-
+
foreach ($innerList as $inner)
if ($inner)
$ids[] = $inner->getId();
-
+
// finding first available inner object
foreach ($innerList as $inner)
if ($inner)
break;
-
+
if (!$inner)
continue;
-
+
// put yet unmapped objects into dao's identityMap
$inner->dao()->getListByIds($ids);
-
+
$skippedMap = $this->skipList[$this->depth];
-
+
$i = $j = 0;
-
+
foreach ($objectList as $object) {
$objectId = $object->getId();
-
+
if (isset($skippedMap[$objectId])) {
if ($skippedMap[$objectId] == 1)
unset($skippedMap[$objectId]);
@@ -89,7 +89,7 @@ public function endPrefetch(array $objectList)
++$j;
continue;
}
-
+
if ($innerList[$i]) {
try {
// avoid dao "caching" here
@@ -106,58 +106,63 @@ public function endPrefetch(array $objectList)
);
}
}
-
+
++$i;
}
-
+
Assert::isEqual(
$i,
count($objectList) - $j
);
}
-
+
unset($this->skipList[$this->depth], $this->storage[$this->depth--]);
-
+
return $objectList;
}
-
+
public static function makeOnlyObject($className, $array, $prefix = null)
{
return self::assemblyObject(new $className, $array, $prefix);
}
-
+
public static function completeObject(Prototyped $object)
{
return self::fetchEncapsulants($object);
}
-
+
final public function getPropertyList()
{
static $lists = array();
-
+
$className = get_class($this);
-
+
if (!isset($lists[$className])) {
$lists[$className] = $this->makePropertyList();
}
-
+
return $lists[$className];
}
-
- final public function getExpandedPropertyList($prefix = null)
+
+ final public function getExpandedPropertyList($prefix = null, $classParent = null)
{
static $lists = array();
-
+
$className = get_class($this);
-
+ if ($classParent) {
+ $className = $classParent . ':' . $className;
+ }
+
if (!isset($lists[$className])) {
+ $lists[$className] = array();
foreach ($this->makePropertyList() as $property) {
if ($property instanceof InnerMetaProperty) {
$lists[$className] =
array_merge(
$lists[$className],
$property->getProto()->getExpandedPropertyList(
- $property->getName().':'
+ $property->getName().':',
+ $className
)
);
} else {
@@ -170,10 +175,10 @@ final public function getExpandedPropertyList($prefix = null)
}
}
}
-
+
return $lists[$className];
}
-
+
/**
* @return LightMetaProperty
* @throws MissingElementException
@@ -182,31 +187,46 @@ public function getPropertyByName($name)
{
if ($property = $this->safePropertyGet($name))
return $property;
-
+
throw new MissingElementException(
"unknown property requested by name '{$name}'"
);
}
-
+
public function isPropertyExists($name)
{
return $this->safePropertyGet($name) !== null;
}
-
+
/**
* @return Form
**/
- public function makeForm($prefix = null)
+ public function makeForm($prefix = null, $includeManyRelations = true)
{
$form = Form::create();
-
+
+ /** @var $property LightMetaProperty */
foreach ($this->getPropertyList() as $property) {
+ if ($this->isIgnoreWhenMakeForm($property, $includeManyRelations)) {
+ continue;
+ }
$property->fillForm($form, $prefix);
}
-
+
return $form;
}
-
+
+ /**
+ * Возвращает true, если свойство не должно быть включено в форму
+ * Вынесено в отдельный метод, чтобы не переопределять весь makeForm
+ * @param LightMetaProperty $property
+ * @param bool $includeManyRelations
+ * @return bool
+ */
+ protected function isIgnoreWhenMakeForm(LightMetaProperty $property, $includeManyRelations = true) {
+ return $property->getRelationId() > MetaRelation::ONE_TO_ONE && !$includeManyRelations;
+ }
+
/**
* @return InsertOrUpdateQuery
**/
@@ -215,18 +235,21 @@ public function fillQuery(
)
{
foreach ($this->getPropertyList() as $property) {
- $property->fillQuery($query, $object);
+ /** @var $property LightMetaProperty */
+ if ($property->getColumnName()) {
+ $property->fillQuery($query, $object);
+ }
}
-
+
return $query;
}
-
+
public function getMapping()
{
static $mappings = array();
-
+
$className = get_class($this);
-
+
if (!isset($mappings[$className])) {
$mapping = array();
foreach ($this->getPropertyList() as $property) {
@@ -234,10 +257,10 @@ public function getMapping()
}
$mappings[$className] = $mapping;
}
-
+
return $mappings[$className];
}
-
+
public function importPrimitive(
$path,
Form $form,
@@ -253,7 +276,7 @@ public function importPrimitive(
} else {
$property = $this->getPropertyByName($path);
$getter = $property->getGetter();
-
+
if (
!$property->isFormless()
&& ($property->getFetchStrategyId() == FetchStrategy::LAZY)
@@ -261,17 +284,17 @@ public function importPrimitive(
) {
return $object;
}
-
+
$value = $object->$getter();
-
+
if (!$ignoreNull || ($value !== null)) {
$form->importValue($prm->getName(), $value);
}
}
-
+
return $object;
}
-
+
public function exportPrimitive(
$path,
BasePrimitive $prm,
@@ -287,16 +310,16 @@ public function exportPrimitive(
$property = $this->getPropertyByName($path);
$setter = $property->getSetter();
$value = $prm->getValue();
-
+
if (
!$ignoreNull || ($value !== null)
) {
if ($property->isIdentifier()) {
$value = $value->getId();
}
-
+
$dropper = $property->getDropper();
-
+
if (
($value === null)
&& method_exists($object, $dropper)
@@ -309,7 +332,7 @@ public function exportPrimitive(
)
) {
$object->$dropper();
-
+
return $object;
} elseif (
(
@@ -322,24 +345,24 @@ public function exportPrimitive(
) {
if ($value === null)
$value = array();
-
+
$getter = $property->getGetter();
$object->$getter()->setList($value);
-
+
return $object;
}
-
+
$object->$setter($value);
}
}
-
+
return $object;
}
-
+
private static function fetchEncapsulants(Prototyped $object)
{
$proto = $object->proto();
-
+
foreach ($proto->getPropertyList() as $property) {
if (
$property->getRelationId() == MetaRelation::ONE_TO_ONE
@@ -347,7 +370,7 @@ private static function fetchEncapsulants(Prototyped $object)
) {
$getter = $property->getGetter();
$setter = $property->getSetter();
-
+
if (($inner = $object->$getter()) instanceof DAOConnected) {
if ($proto->depth)
$proto->storage[$proto->depth][$setter][] = $inner;
@@ -365,11 +388,11 @@ private static function fetchEncapsulants(Prototyped $object)
$proto->storage[$proto->depth][$setter][] = null;
}
}
-
+
return $object;
}
-
- private static function assemblyObject(
+
+ final protected static function assemblyObject(
Prototyped $object, $array, $prefix = null
)
{
@@ -377,12 +400,14 @@ private static function assemblyObject(
$dao = $object->dao();
else
$dao = null;
-
+
$proto = $object->proto();
-
+
+ try {
+
foreach ($proto->getPropertyList() as $property) {
$setter = $property->getSetter();
-
+
if ($property instanceof InnerMetaProperty) {
$object->$setter(
$property->toValue($dao, $array, $prefix)
@@ -394,21 +419,29 @@ private static function assemblyObject(
== FetchStrategy::LAZY
) {
$columnName = $prefix.$property->getColumnName();
-
+
$object->
{$setter.'Id'}($array[$columnName]);
-
+
continue;
}
}
-
+
$object->$setter($property->toValue($dao, $array, $prefix));
}
}
-
+
+ } catch( Exception $e ) {
+// echo '';
+// echo $e->getTraceAsString();
+// echo '
';
+// die('error');
+ throw $e;
+ }
+
return $object;
}
-
+
private function forwardPrimitive(
$path,
Form $form = null,
@@ -418,13 +451,13 @@ private function forwardPrimitive(
)
{
list($propertyName, $path) = explode(':', $path, 2);
-
+
$property = $this->getPropertyByName($propertyName);
-
+
Assert::isTrue($property instanceof InnerMetaProperty);
-
+
$getter = $property->getGetter();
-
+
if ($form)
return $property->getProto()->importPrimitive(
$path, $form, $prm, $object->$getter(), $ignoreNull
@@ -434,14 +467,14 @@ private function forwardPrimitive(
$path, $prm, $object->$getter(), $ignoreNull
);
}
-
+
private function safePropertyGet($name)
{
$list = $this->getPropertyList();
-
- if (isset($list[$name]))
+
+ if (is_string($name) && isset($list[$name]))
return $list[$name];
-
+
return null;
}
}
diff --git a/main/Base/BaseRange.class.php b/main/Base/BaseRange.class.php
old mode 100644
new mode 100755
diff --git a/main/Base/CalendarDay.class.php b/main/Base/CalendarDay.class.php
old mode 100644
new mode 100755
index b3a718951d..6923975ab3
--- a/main/Base/CalendarDay.class.php
+++ b/main/Base/CalendarDay.class.php
@@ -29,7 +29,10 @@ public static function create($timestamp)
public function __sleep()
{
- return array('int', 'selected', 'outside');
+ $sleep = parent::__sleep();
+ $sleep[] = 'selected';
+ $sleep[] = 'outside';
+ return $sleep;
}
public function isSelected()
diff --git a/main/Base/CalendarMonthWeekly.class.php b/main/Base/CalendarMonthWeekly.class.php
old mode 100644
new mode 100755
diff --git a/main/Base/CalendarWeek.class.php b/main/Base/CalendarWeek.class.php
old mode 100644
new mode 100755
diff --git a/main/Base/CallChain.class.php b/main/Base/CallChain.class.php
old mode 100644
new mode 100755
diff --git a/main/Base/Collection.class.php b/main/Base/Collection.class.php
old mode 100644
new mode 100755
diff --git a/main/Base/CollectionItem.class.php b/main/Base/CollectionItem.class.php
old mode 100644
new mode 100755
diff --git a/main/Base/Comparator.class.php b/main/Base/Comparator.class.php
old mode 100644
new mode 100755
diff --git a/main/Base/DateObjectComparator.class.php b/main/Base/DateObjectComparator.class.php
old mode 100644
new mode 100755
diff --git a/main/Base/DateRange.class.php b/main/Base/DateRange.class.php
old mode 100644
new mode 100755
diff --git a/main/Base/FloatRange.class.php b/main/Base/FloatRange.class.php
old mode 100644
new mode 100755
diff --git a/main/Base/Hstore.class.php b/main/Base/Hstore.class.php
old mode 100644
new mode 100755
index 48bce8690d..9896ac51ea
--- a/main/Base/Hstore.class.php
+++ b/main/Base/Hstore.class.php
@@ -12,7 +12,7 @@
/**
* @ingroup Helpers
**/
- final class Hstore implements Stringable
+ class Hstore implements Stringable
{
protected $properties = array();
@@ -87,6 +87,10 @@ public function isExists($key)
{
return isset($this->properties[$key]);
}
+
+ public function has($key) {
+ return $this->isExists($key);
+ }
/**
* @return Hstore
@@ -97,9 +101,16 @@ public function toValue($raw)
return $this;
$return = null;
- eval("\$return = array({$raw});");
- $this->properties = $return;
-
+ try {
+ $prepared = str_replace('$', '\\$', $raw);
+ if (@eval("\$return = array({$prepared});") !== false) {
+ $this->properties = $return;
+ }
+
+ } catch (Exception $e) {
+ // temporary - skip errors on malformed hstore data
+ }
+
return $this;
}
diff --git a/main/Base/IdentifiableTree.class.php b/main/Base/IdentifiableTree.class.php
old mode 100644
new mode 100755
diff --git a/main/Base/ImageType.class.php b/main/Base/ImageType.class.php
old mode 100644
new mode 100755
diff --git a/main/Base/ImmutableObjectComparator.class.php b/main/Base/ImmutableObjectComparator.class.php
old mode 100644
new mode 100755
diff --git a/main/Base/InnerMetaProperty.class.php b/main/Base/InnerMetaProperty.class.php
old mode 100644
new mode 100755
diff --git a/main/Base/IntegerSet.class.php b/main/Base/IntegerSet.class.php
old mode 100644
new mode 100755
diff --git a/main/Base/IsoCurrency.class.php b/main/Base/IsoCurrency.class.php
old mode 100644
new mode 100755
diff --git a/main/Base/LightMetaProperty.class.php b/main/Base/LightMetaProperty.class.php
old mode 100644
new mode 100755
index e3a9225c54..0c0fdcf9b8
--- a/main/Base/LightMetaProperty.class.php
+++ b/main/Base/LightMetaProperty.class.php
@@ -18,7 +18,7 @@
class LightMetaProperty implements Stringable
{
const UNSIGNED_FLAG = 0x1000;
-
+
private static $limits = array(
0x0002 => array(
PrimitiveInteger::SIGNED_SMALL_MIN,
@@ -45,34 +45,35 @@ class LightMetaProperty implements Stringable
null
)
);
-
+
private $name = null;
private $columnName = null;
-
+
private $type = null;
private $className = null;
-
+
private $size = null;
-
+
private $min = null;
private $max = null;
-
+
private $required = false;
private $generic = false;
private $inner = false;
-
+
/// @see MetaRelation
private $relationId = null;
-
+
/// @see FetchStrategy
private $strategyId = null;
-
+
private $getter = null;
private $setter = null;
private $dropper = null;
-
+ private $defaulter = null;
+
private $identifier = null;
-
+
/**
* @return LightMetaProperty
**/
@@ -80,7 +81,7 @@ public static function create()
{
return new self;
}
-
+
/**
* must by in sync with InnerMetaProperty::make()
*
@@ -93,26 +94,29 @@ public static function fill(
)
{
$property->name = $name;
-
+
$methodSuffix = ucfirst($name);
$property->getter = 'get'.$methodSuffix;
$property->setter = 'set'.$methodSuffix;
$property->dropper = 'drop'.$methodSuffix;
-
+ $property->defaulter = 'getDefault'.$methodSuffix;
+
if ($columnName)
$property->columnName = $columnName;
- else
+ else if ($columnName === null)
$property->columnName = $name;
-
+
$property->type = $type;
$property->className = $className;
-
+
if ($size) {
if (
($type == 'integer')
|| ($type == 'identifier') // obsoleted
|| ($type == 'integerIdentifier')
|| ($type == 'enumeration')
+ || ($type == 'enum')
+ || ($type == 'registry')
) {
$property->min = self::$limits[$size][0];
$property->max = self::$limits[$size][1];
@@ -121,147 +125,153 @@ public static function fill(
} elseif ($type != 'float') { // string
$property->max = $size;
}
-
+
$property->size = $size;
}
-
+
$property->required = $required;
$property->generic = $generic;
$property->inner = $inner;
-
+
$property->relationId = $relationId;
$property->strategyId = $strategyId;
-
+
$property->identifier =
$generic && $required && (
($type == 'identifier') // obsoleted
|| ($type == 'integerIdentifier')
|| ($type == 'scalarIdentifier')
+ || ($type == 'uuidIdentifier')
);
-
+
return $property;
}
-
+
public function getName()
{
return $this->name;
}
-
+
public function getColumnName()
{
return $this->columnName;
}
-
+
public function getGetter()
{
return $this->getter;
}
-
+
public function getSetter()
{
return $this->setter;
}
-
+
public function getDropper()
{
return $this->dropper;
}
-
+
+ public function getDefaulter()
+ {
+ return $this->defaulter;
+ }
+
/**
* @return LightMetaProperty
**/
public function setColumnName($name)
{
$this->columnName = $name;
-
+
return $this;
}
-
+
public function getClassName()
{
return $this->className;
}
-
+
public function getMin()
{
return $this->min;
}
-
+
public function getMax()
{
return $this->max;
}
-
+
public function getType()
{
return $this->type;
}
-
+
public function isRequired()
{
return $this->required;
}
-
+
/**
* @return LightMetaProperty
**/
public function setRequired($yrly)
{
$this->required = $yrly;
-
+
return $this;
}
-
+
public function isGenericType()
{
return $this->generic;
}
-
+
public function isInner()
{
return $this->inner;
}
-
+
public function getRelationId()
{
return $this->relationId;
}
-
+
public function getFetchStrategyId()
{
return $this->strategyId;
}
-
+
/**
* @return LightMetaProperty
**/
public function setFetchStrategy(FetchStrategy $strategy)
{
$this->strategyId = $strategy->getId();
-
+
return $this;
}
-
+
/**
* @return LightMetaProperty
**/
public function dropFetchStrategy()
{
$this->strategyId = null;
-
+
return $this;
}
-
+
public function getContainerName($holderName)
{
return $holderName.ucfirst($this->getName()).'DAO';
}
-
+
public function isBuildable($array, $prefix = null)
{
$column = $prefix.$this->columnName;
$exists = isset($array[$column]);
-
+
if (
$this->relationId
|| $this->generic
@@ -272,7 +282,7 @@ public function isBuildable($array, $prefix = null)
&& !$this->generic
)
return false;
-
+
if ($this->required) {
Assert::isTrue(
$exists,
@@ -282,38 +292,51 @@ public function isBuildable($array, $prefix = null)
return false;
}
}
-
+
return true;
}
-
+
/**
* @return BasePrimitive
**/
public function makePrimitive($name)
{
- $prm =
- call_user_func(
- array('Primitive', $this->type),
- $name
- );
-
+
+ if ($this->isTranslated()) {
+ $prm =
+ call_user_func(
+ array('Primitive', 'string'),
+ $name
+ );
+ } else {
+ $prm =
+ call_user_func(
+ array('Primitive', $this->type),
+ $name
+ );
+ }
+
if (null !== ($min = $this->getMin()))
$prm->setMin($min);
-
+
if (null !== ($max = $this->getMax()))
$prm->setMax($max);
-
+
if ($prm instanceof IdentifiablePrimitive)
$prm->of($this->className);
-
+
if ($this->required)
$prm->required();
-
+
return $prm;
}
-
+
public function fillMapping(array $mapping)
{
+ if (empty($this->columnName)) {
+ return $mapping;
+ }
+
if (
!$this->relationId
|| (
@@ -326,10 +349,10 @@ public function fillMapping(array $mapping)
) {
$mapping[$this->name] = $this->columnName;
}
-
+
return $mapping;
}
-
+
/**
* @return Form
**/
@@ -339,7 +362,7 @@ public function fillForm(Form $form, $prefix = null)
$this->makePrimitive($prefix.$this->name)
);
}
-
+
/**
* @return InsertOrUpdateQuery
**/
@@ -369,29 +392,71 @@ public function fillQuery(
}
$value = $object->{$getter}();
-
+
if ($this->type == 'binary') {
$query->set($this->columnName, new DBBinary($value));
+ } elseif($this->type == 'arrayOfIntegers') {
+ $query->set($this->columnName, DBArray::create($value)->integers());
+ } elseif($this->type == 'arrayOfFloats') {
+ $query->set($this->columnName, DBArray::create($value)->floats());
+ } elseif($this->type == 'arrayOfStrings') {
+ $query->set($this->columnName, DBArray::create($value)->strings());
+ } elseif($this->type == 'json' ) {
+ $query->set($this->columnName, DBArray::create($value)->json());
+ } elseif($this->type == 'jsonb' ) {
+ $query->set($this->columnName, DBArray::create($value)->jsonb());
} else {
$query->lazySet($this->columnName, $value);
}
}
-
+
return $query;
}
-
+
public function toValue(ProtoDAO $dao = null, $array, $prefix = null)
{
$raw = $array[$prefix.$this->columnName];
-
+
if ($this->type == 'binary') {
return DBPool::getByDao($dao)->getDialect()->unquoteBinary($raw);
}
-
+
if ($this->className == 'HttpUrl') {
return HttpUrl::create()->parse($raw);
}
-
+
+ if($this->type == 'set') {
+ // MongoDB driver compatibility
+ if (is_array($raw)) {
+ return $raw;
+ } else {
+ throw new WrongArgumentException('raw data is not array!');
+ }
+ }
+
+ if (strpos($this->type, 'arrayOf') !== false) {
+ // PgSQL driver compatibility
+ $matches = array();
+ if ($this->type == 'arrayOfIntegers') {
+ $mappingFunction = 'intval';
+ } else if ($this->type == 'arrayOfFloats') {
+ $mappingFunction = 'doubleval';
+ } else if ($this->type == 'arrayOfStrings') {
+ $mappingFunction = 'stripslashes';
+ } else {
+ throw new WrongArgumentException('unknown array type');
+ }
+ if (preg_match('/^{(.*)}$/', $raw, $matches)) {
+ return array_map($mappingFunction, str_getcsv($matches[1]));
+ } else {
+ throw new WrongArgumentException('raw data is not compatible with ' . $this->type);
+ }
+ }
+
+ if ($this->type == 'json' || $this->type == 'jsonb') {
+ return json_decode($raw, true); //associative array insteaFd of object
+ }
+
if (
!$this->identifier
&& $this->generic
@@ -404,20 +469,24 @@ public function toValue(ProtoDAO $dao = null, $array, $prefix = null)
) {
// BOVM: prevents segfault on >=php-5.2.5
Assert::classExists($this->className);
-
- if (!is_subclass_of($this->className, 'Enumeration')) {
+
+ if (
+ !is_subclass_of($this->className, 'Enumeration')
+ && !is_subclass_of($this->className, 'Enum')
+ && !is_subclass_of($this->className, 'Registry')
+ ) {
$remoteDao = call_user_func(array($this->className, 'dao'));
-
+
$joinPrefix = $remoteDao->getJoinPrefix(
$this->columnName,
$prefix
);
-
+
$joined = (
($this->strategyId == FetchStrategy::JOIN)
|| isset($array[$joinPrefix.$remoteDao->getIdName()])
);
-
+
if ($joined) {
return $remoteDao->makeObject($array, $joinPrefix);
} else {
@@ -425,28 +494,28 @@ public function toValue(ProtoDAO $dao = null, $array, $prefix = null)
// by AbstractProtoClass::fetchEncapsulants
$object = new $this->className;
$object->setId($raw);
-
+
return $object;
}
} else {
return new $this->className($raw);
}
}
-
+
// veeeeery "special" handling, by tradition.
// MySQL returns 0/1, others - t/f
if ($this->type == 'boolean') {
return (bool) strtr($raw, array('f' => null));
}
-
+
return $raw;
}
-
+
public function isIdentifier()
{
return $this->identifier;
}
-
+
final public function toString()
{
return
@@ -503,11 +572,22 @@ final public function toString()
)
.')';
}
-
+
public function isFormless()
{
// NOTE: enum here formless types
- return ($this->type == 'enumeration');
+ return in_array(
+ $this->type,
+ array(
+ 'enumeration',
+ 'enum',
+ 'registry',
+ )
+ );
}
+
+ public function isTranslated() {
+ return 'TranslatedStore' == $this->className;
+ }
}
?>
\ No newline at end of file
diff --git a/main/Base/MimeType.class.php b/main/Base/MimeType.class.php
new file mode 100644
index 0000000000..b9a678435c
--- /dev/null
+++ b/main/Base/MimeType.class.php
@@ -0,0 +1,860 @@
+ 'application/andrew-inset',
+ 2 => 'application/annodex',
+ 3 => 'application/atom+xml',
+ 4 => 'application/atomcat+xml',
+ 5 => 'application/atomserv+xml',
+ 6 => 'application/bbolin',
+ 7 => 'application/cap',
+ 8 => 'application/cu-seeme',
+ 9 => 'application/davmount+xml',
+ 10 => 'application/dsptype',
+ 11 => 'application/ecmascript',
+ 12 => 'application/futuresplash',
+ 13 => 'application/hta',
+ 14 => 'application/java-archive',
+ 15 => 'application/java-serialized-object',
+ 16 => 'application/java-vm',
+ 17 => 'application/javascript',
+ 18 => 'application/m3g',
+ 19 => 'application/mac-binhex40',
+ 20 => 'application/mac-compactpro',
+ 21 => 'application/mathematica',
+ 22 => 'application/msaccess',
+ 23 => 'application/msword',
+ 24 => 'application/mxf',
+ 25 => 'application/octet-stream',
+ 26 => 'application/oda',
+ 27 => 'application/ogg',
+ 28 => 'application/pdf',
+ 29 => 'application/pgp-keys',
+ 30 => 'application/pgp-signature',
+ 31 => 'application/pics-rules',
+ 32 => 'application/postscript',
+ 33 => 'application/rar',
+ 34 => 'application/rdf+xml',
+ 35 => 'application/rss+xml',
+ 36 => 'application/rtf',
+ 37 => 'application/smil',
+ 38 => 'application/xhtml+xml',
+ 39 => 'application/xml',
+ 40 => 'application/xspf+xml',
+ 41 => 'application/zip',
+ 42 => 'application/vnd.android.package-archive',
+ 43 => 'application/vnd.cinderella',
+ 44 => 'application/vnd.google-earth.kml+xml',
+ 45 => 'application/vnd.google-earth.kmz',
+ 46 => 'application/vnd.mozilla.xul+xml',
+ 47 => 'application/vnd.ms-excel',
+ 48 => 'application/vnd.ms-pki.seccat',
+ 49 => 'application/vnd.ms-pki.stl',
+ 50 => 'application/vnd.ms-powerpoint',
+ 51 => 'application/vnd.oasis.opendocument.chart',
+ 52 => 'application/vnd.oasis.opendocument.database',
+ 53 => 'application/vnd.oasis.opendocument.formula',
+ 54 => 'application/vnd.oasis.opendocument.graphics',
+ 55 => 'application/vnd.oasis.opendocument.graphics-template',
+ 56 => 'application/vnd.oasis.opendocument.image',
+ 57 => 'application/vnd.oasis.opendocument.presentation',
+ 58 => 'application/vnd.oasis.opendocument.presentation-template',
+ 59 => 'application/vnd.oasis.opendocument.spreadsheet',
+ 60 => 'application/vnd.oasis.opendocument.spreadsheet-template',
+ 61 => 'application/vnd.oasis.opendocument.text',
+ 62 => 'application/vnd.oasis.opendocument.text-master',
+ 63 => 'application/vnd.oasis.opendocument.text-template',
+ 64 => 'application/vnd.oasis.opendocument.text-web',
+ 65 => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
+ 66 => 'application/vnd.openxmlformats-officedocument.spreadsheetml.template',
+ 67 => 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
+ 68 => 'application/vnd.openxmlformats-officedocument.presentationml.slideshow',
+ 69 => 'application/vnd.openxmlformats-officedocument.presentationml.template',
+ 70 => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
+ 71 => 'application/vnd.openxmlformats-officedocument.wordprocessingml.template',
+ 72 => 'application/vnd.rim.cod',
+ 73 => 'application/vnd.smaf',
+ 74 => 'application/vnd.stardivision.calc',
+ 75 => 'application/vnd.stardivision.chart',
+ 76 => 'application/vnd.stardivision.draw',
+ 77 => 'application/vnd.stardivision.impress',
+ 78 => 'application/vnd.stardivision.math',
+ 79 => 'application/vnd.stardivision.writer',
+ 80 => 'application/vnd.stardivision.writer-global',
+ 81 => 'application/vnd.sun.xml.calc',
+ 82 => 'application/vnd.sun.xml.calc.template',
+ 83 => 'application/vnd.sun.xml.draw',
+ 84 => 'application/vnd.sun.xml.draw.template',
+ 85 => 'application/vnd.sun.xml.impress',
+ 86 => 'application/vnd.sun.xml.impress.template',
+ 87 => 'application/vnd.sun.xml.math',
+ 88 => 'application/vnd.sun.xml.writer',
+ 89 => 'application/vnd.sun.xml.writer.global',
+ 90 => 'application/vnd.sun.xml.writer.template',
+ 91 => 'application/vnd.symbian.install',
+ 92 => 'application/vnd.visio',
+ 93 => 'application/vnd.wap.wbxml',
+ 94 => 'application/vnd.wap.wmlc',
+ 95 => 'application/vnd.wap.wmlscriptc',
+ 96 => 'application/vnd.wordperfect',
+ 97 => 'application/vnd.wordperfect5.1',
+ 98 => 'application/x-123',
+ 99 => 'application/x-7z-compressed',
+ 100 => 'application/x-abiword',
+ 101 => 'application/x-apple-diskimage',
+ 102 => 'application/x-bcpio',
+ 103 => 'application/x-bittorrent',
+ 104 => 'application/x-cab',
+ 105 => 'application/x-cbr',
+ 106 => 'application/x-cbz',
+ 107 => 'application/x-cdf',
+ 108 => 'application/x-cdlink',
+ 109 => 'application/x-chess-pgn',
+ 110 => 'application/x-cpio',
+ 111 => 'application/x-csh',
+ 112 => 'application/x-debian-package',
+ 113 => 'application/x-director',
+ 114 => 'application/x-dms',
+ 115 => 'application/x-doom',
+ 116 => 'application/x-dvi',
+ 117 => 'application/x-httpd-eruby',
+ 118 => 'application/x-font',
+ 119 => 'application/x-freemind',
+ 120 => 'application/x-futuresplash',
+ 121 => 'application/x-gnumeric',
+ 122 => 'application/x-go-sgf',
+ 123 => 'application/x-graphing-calculator',
+ 124 => 'application/x-gtar',
+ 125 => 'application/x-hdf',
+ 126 => 'application/x-httpd-php',
+ 127 => 'application/x-httpd-php-source',
+ 128 => 'application/x-httpd-php3',
+ 129 => 'application/x-httpd-php3-preprocessed',
+ 130 => 'application/x-httpd-php4',
+ 131 => 'application/x-httpd-php5',
+ 132 => 'application/x-ica',
+ 133 => 'application/x-info',
+ 134 => 'application/x-internet-signup',
+ 135 => 'application/x-iphone',
+ 136 => 'application/x-iso9660-image',
+ 137 => 'application/x-jam',
+ 138 => 'application/x-java-jnlp-file',
+ 139 => 'application/x-jmol',
+ 140 => 'application/x-kchart',
+ 141 => 'application/x-killustrator',
+ 142 => 'application/x-koan',
+ 143 => 'application/x-kpresenter',
+ 144 => 'application/x-kspread',
+ 145 => 'application/x-kword',
+ 146 => 'application/x-latex',
+ 147 => 'application/x-lha',
+ 148 => 'application/x-lyx',
+ 149 => 'application/x-lzh',
+ 150 => 'application/x-lzx',
+ 151 => 'application/x-maker',
+ 152 => 'application/x-mif',
+ 153 => 'application/x-ms-wmd',
+ 154 => 'application/x-ms-wmz',
+ 155 => 'application/x-msdos-program',
+ 156 => 'application/x-msi',
+ 157 => 'application/x-netcdf',
+ 158 => 'application/x-ns-proxy-autoconfig',
+ 159 => 'application/x-nwc',
+ 160 => 'application/x-object',
+ 161 => 'application/x-oz-application',
+ 162 => 'application/x-pkcs7-certreqresp',
+ 163 => 'application/x-pkcs7-crl',
+ 164 => 'application/x-python-code',
+ 165 => 'application/x-qgis',
+ 166 => 'application/x-quicktimeplayer',
+ 167 => 'application/x-redhat-package-manager',
+ 168 => 'application/x-ruby',
+ 169 => 'application/x-sh',
+ 170 => 'application/x-shar',
+ 171 => 'application/x-shockwave-flash',
+ 172 => 'application/x-silverlight',
+ 173 => 'application/x-stuffit',
+ 174 => 'application/x-sv4cpio',
+ 175 => 'application/x-sv4crc',
+ 176 => 'application/x-tar',
+ 177 => 'application/x-tcl',
+ 178 => 'application/x-tex-gf',
+ 179 => 'application/x-tex-pk',
+ 180 => 'application/x-texinfo',
+ 181 => 'application/x-troff',
+ 182 => 'application/x-troff-man',
+ 183 => 'application/x-troff-me',
+ 184 => 'application/x-troff-ms',
+ 185 => 'application/x-ustar',
+ 186 => 'application/x-wais-source',
+ 187 => 'application/x-wingz',
+ 188 => 'application/x-x509-ca-cert',
+ 189 => 'application/x-xcf',
+ 190 => 'application/x-xfig',
+ 191 => 'application/x-xpinstall',
+ 192 => 'audio/amr',
+ 193 => 'audio/amr-wb',
+ 194 => 'audio/amr',
+ 195 => 'audio/amr-wb',
+ 196 => 'audio/annodex',
+ 197 => 'audio/basic',
+ 198 => 'audio/flac',
+ 199 => 'audio/midi',
+ 200 => 'audio/mpeg',
+ 201 => 'audio/mpegurl',
+ 202 => 'audio/ogg',
+ 203 => 'audio/prs.sid',
+ 204 => 'audio/x-aiff',
+ 205 => 'audio/x-gsm',
+ 206 => 'audio/x-mpegurl',
+ 207 => 'audio/x-ms-wma',
+ 208 => 'audio/x-ms-wax',
+ 209 => 'audio/x-pn-realaudio',
+ 210 => 'audio/x-realaudio',
+ 211 => 'audio/x-scpls',
+ 212 => 'audio/x-sd2',
+ 213 => 'audio/x-wav',
+ 214 => 'chemical/x-alchemy',
+ 215 => 'chemical/x-cache',
+ 216 => 'chemical/x-cache-csf',
+ 217 => 'chemical/x-cactvs-binary',
+ 218 => 'chemical/x-cdx',
+ 219 => 'chemical/x-cerius',
+ 220 => 'chemical/x-chem3d',
+ 221 => 'chemical/x-chemdraw',
+ 222 => 'chemical/x-cif',
+ 223 => 'chemical/x-cmdf',
+ 224 => 'chemical/x-cml',
+ 225 => 'chemical/x-compass',
+ 226 => 'chemical/x-crossfire',
+ 227 => 'chemical/x-csml',
+ 228 => 'chemical/x-ctx',
+ 229 => 'chemical/x-cxf',
+ 230 => 'chemical/x-daylight-smiles',
+ 231 => 'chemical/x-embl-dl-nucleotide',
+ 232 => 'chemical/x-galactic-spc',
+ 233 => 'chemical/x-gamess-input',
+ 234 => 'chemical/x-gaussian-checkpoint',
+ 235 => 'chemical/x-gaussian-cube',
+ 236 => 'chemical/x-gaussian-input',
+ 237 => 'chemical/x-gaussian-log',
+ 238 => 'chemical/x-gcg8-sequence',
+ 239 => 'chemical/x-genbank',
+ 240 => 'chemical/x-hin',
+ 241 => 'chemical/x-isostar',
+ 242 => 'chemical/x-jcamp-dx',
+ 243 => 'chemical/x-kinemage',
+ 244 => 'chemical/x-macmolecule',
+ 245 => 'chemical/x-macromodel-input',
+ 246 => 'chemical/x-mdl-molfile',
+ 247 => 'chemical/x-mdl-rdfile',
+ 248 => 'chemical/x-mdl-rxnfile',
+ 249 => 'chemical/x-mdl-sdfile',
+ 250 => 'chemical/x-mdl-tgf',
+ 251 => 'chemical/x-mif',
+ 252 => 'chemical/x-mmcif',
+ 253 => 'chemical/x-mol2',
+ 254 => 'chemical/x-molconn-Z',
+ 255 => 'chemical/x-mopac-graph',
+ 256 => 'chemical/x-mopac-input',
+ 257 => 'chemical/x-mopac-out',
+ 258 => 'chemical/x-mopac-vib',
+ 259 => 'chemical/x-ncbi-asn1',
+ 260 => 'chemical/x-ncbi-asn1-ascii',
+ 261 => 'chemical/x-ncbi-asn1-binary',
+ 262 => 'chemical/x-ncbi-asn1-spec',
+ 263 => 'chemical/x-pdb',
+ 264 => 'chemical/x-rosdal',
+ 265 => 'chemical/x-swissprot',
+ 266 => 'chemical/x-vamas-iso14976',
+ 267 => 'chemical/x-vmd',
+ 268 => 'chemical/x-xtel',
+ 269 => 'chemical/x-xyz',
+ 270 => 'image/gif',
+ 271 => 'image/ief',
+ 272 => 'image/jpeg',
+ 273 => 'image/pcx',
+ 274 => 'image/png',
+ 275 => 'image/svg+xml',
+ 276 => 'image/tiff',
+ 277 => 'image/vnd.djvu',
+ 278 => 'image/vnd.wap.wbmp',
+ 279 => 'image/x-canon-cr2',
+ 280 => 'image/x-canon-crw',
+ 281 => 'image/x-cmu-raster',
+ 282 => 'image/x-coreldraw',
+ 283 => 'image/x-coreldrawpattern',
+ 284 => 'image/x-coreldrawtemplate',
+ 285 => 'image/x-corelphotopaint',
+ 286 => 'image/x-epson-erf',
+ 287 => 'image/x-icon',
+ 288 => 'image/x-jg',
+ 289 => 'image/x-jng',
+ 290 => 'image/x-ms-bmp',
+ 291 => 'image/x-nikon-nef',
+ 292 => 'image/x-olympus-orf',
+ 293 => 'image/x-photoshop',
+ 294 => 'image/x-portable-anymap',
+ 295 => 'image/x-portable-bitmap',
+ 296 => 'image/x-portable-graymap',
+ 297 => 'image/x-portable-pixmap',
+ 298 => 'image/x-rgb',
+ 299 => 'image/x-xbitmap',
+ 300 => 'image/x-xpixmap',
+ 301 => 'image/x-xwindowdump',
+ 302 => 'message/rfc822',
+ 303 => 'model/iges',
+ 304 => 'model/mesh',
+ 305 => 'model/vrml',
+ 306 => 'model/x3d+vrml',
+ 307 => 'model/x3d+xml',
+ 308 => 'model/x3d+binary',
+ 309 => 'text/cache-manifest',
+ 310 => 'text/calendar',
+ 311 => 'text/css',
+ 312 => 'text/csv',
+ 313 => 'text/h323',
+ 314 => 'text/html',
+ 315 => 'text/iuls',
+ 316 => 'text/mathml',
+ 317 => 'text/plain',
+ 318 => 'text/richtext',
+ 319 => 'text/scriptlet',
+ 320 => 'text/texmacs',
+ 321 => 'text/tab-separated-values',
+ 322 => 'text/vnd.sun.j2me.app-descriptor',
+ 323 => 'text/vnd.wap.wml',
+ 324 => 'text/vnd.wap.wmlscript',
+ 325 => 'text/x-bibtex',
+ 326 => 'text/x-boo',
+ 327 => 'text/x-c++hdr',
+ 328 => 'text/x-c++src',
+ 329 => 'text/x-chdr',
+ 330 => 'text/x-component',
+ 331 => 'text/x-csh',
+ 332 => 'text/x-csrc',
+ 333 => 'text/x-dsrc',
+ 334 => 'text/x-diff',
+ 335 => 'text/x-haskell',
+ 336 => 'text/x-java',
+ 337 => 'text/x-literate-haskell',
+ 338 => 'text/x-moc',
+ 339 => 'text/x-pascal',
+ 340 => 'text/x-pcs-gcd',
+ 341 => 'text/x-perl',
+ 342 => 'text/x-python',
+ 343 => 'text/x-scala',
+ 344 => 'text/x-setext',
+ 345 => 'text/x-sh',
+ 346 => 'text/x-tcl',
+ 347 => 'text/x-tex',
+ 348 => 'text/x-vcalendar',
+ 349 => 'text/x-vcard',
+ 350 => 'video/3gpp',
+ 351 => 'video/annodex',
+ 352 => 'video/dl',
+ 353 => 'video/dv',
+ 354 => 'video/fli',
+ 355 => 'video/gl',
+ 356 => 'video/mpeg',
+ 357 => 'video/mp4',
+ 358 => 'video/quicktime',
+ 359 => 'video/ogg',
+ 360 => 'video/vnd.mpegurl',
+ 361 => 'video/x-flv',
+ 362 => 'video/x-la-asf',
+ 363 => 'video/x-mng',
+ 364 => 'video/x-ms-asf',
+ 365 => 'video/x-ms-wm',
+ 366 => 'video/x-ms-wmv',
+ 367 => 'video/x-ms-wmx',
+ 368 => 'video/x-ms-wvx',
+ 369 => 'video/x-msvideo',
+ 370 => 'video/x-sgi-movie',
+ 371 => 'video/x-matroska',
+ 372 => 'x-conference/x-cooltalk',
+ 373 => 'x-epoc/x-sisx-app',
+ 374 => 'x-world/x-vrml',
+ 375 => 'image/jpeg',
+ );
+
+ /*
+ * Extension map
+ */
+ protected static $extensions = array(
+ 1 => 'ez',
+ 2 => 'anx',
+ 3 => 'atom',
+ 4 => 'atomcat',
+ 5 => 'atomsrv',
+ 6 => 'lin',
+ 7 => 'cap',
+ 8 => 'cu',
+ 9 => 'davmount',
+ 10 => 'tsp',
+ 11 => 'es',
+ 12 => 'spl',
+ 13 => 'hta',
+ 14 => 'jar',
+ 15 => 'ser',
+ 16 => 'class',
+ 17 => 'js',
+ 18 => 'm3g',
+ 19 => 'hqx',
+ 20 => 'cpt',
+ 21 => 'nb',
+ 22 => 'mdb',
+ 23 => 'doc',
+ 24 => 'mxf',
+ 25 => 'bin',
+ 26 => 'oda',
+ 27 => 'ogx',
+ 28 => 'pdf',
+ 29 => 'key',
+ 30 => 'pgp',
+ 31 => 'prf',
+ 32 => 'ps',
+ 33 => 'rar',
+ 34 => 'rdf',
+ 35 => 'rss',
+ 36 => 'rtf',
+ 37 => 'smi',
+ 38 => 'xhtml',
+ 39 => 'xml',
+ 40 => 'xspf',
+ 41 => 'zip',
+ 42 => 'apk',
+ 43 => 'cdy',
+ 44 => 'kml',
+ 45 => 'kmz',
+ 46 => 'xul',
+ 47 => 'xls',
+ 48 => 'cat',
+ 49 => 'stl',
+ 50 => 'ppt',
+ 51 => 'odc',
+ 52 => 'odb',
+ 53 => 'odf',
+ 54 => 'odg',
+ 55 => 'otg',
+ 56 => 'odi',
+ 57 => 'odp',
+ 58 => 'otp',
+ 59 => 'ods',
+ 60 => 'ots',
+ 61 => 'odt',
+ 62 => 'odm',
+ 63 => 'ott',
+ 64 => 'oth',
+ 65 => 'xlsx',
+ 66 => 'xltx',
+ 67 => 'pptx',
+ 68 => 'ppsx',
+ 69 => 'potx',
+ 70 => 'docx',
+ 71 => 'dotx',
+ 72 => 'cod',
+ 73 => 'mmf',
+ 74 => 'sdc',
+ 75 => 'sds',
+ 76 => 'sda',
+ 77 => 'sdd',
+ 78 => 'sdf',
+ 79 => 'sdw',
+ 80 => 'sgl',
+ 81 => 'sxc',
+ 82 => 'stc',
+ 83 => 'sxd',
+ 84 => 'std',
+ 85 => 'sxi',
+ 86 => 'sti',
+ 87 => 'sxm',
+ 88 => 'sxw',
+ 89 => 'sxg',
+ 90 => 'stw',
+ 91 => 'sis',
+ 92 => 'vsd',
+ 93 => 'wbxml',
+ 94 => 'wmlc',
+ 95 => 'wmlsc',
+ 96 => 'wpd',
+ 97 => 'wp5',
+ 98 => 'wk',
+ 99 => '7z',
+ 100 => 'abw',
+ 101 => 'dmg',
+ 102 => 'bcpio',
+ 103 => 'torrent',
+ 104 => 'cab',
+ 105 => 'cbr',
+ 106 => 'cbz',
+ 107 => 'cdf',
+ 108 => 'vcd',
+ 109 => 'pgn',
+ 110 => 'cpio',
+ 111 => 'csh',
+ 112 => 'deb',
+ 113 => 'dcr',
+ 114 => 'dms',
+ 115 => 'wad',
+ 116 => 'dvi',
+ 117 => 'rhtml',
+ 118 => 'pfa',
+ 119 => 'mm',
+ 120 => 'spl',
+ 121 => 'gnumeric',
+ 122 => 'sgf',
+ 123 => 'gcf',
+ 124 => 'gtar',
+ 125 => 'hdf',
+ 126 => 'phtml',
+ 127 => 'phps',
+ 128 => 'php3',
+ 129 => 'php3p',
+ 130 => 'php4',
+ 131 => 'php5',
+ 132 => 'ica',
+ 133 => 'info',
+ 134 => 'ins',
+ 135 => 'iii',
+ 136 => 'iso',
+ 137 => 'jam',
+ 138 => 'jnlp',
+ 139 => 'jmz',
+ 140 => 'chrt',
+ 141 => 'kil',
+ 142 => 'skp',
+ 143 => 'kpr',
+ 144 => 'ksp',
+ 145 => 'kwd',
+ 146 => 'latex',
+ 147 => 'lha',
+ 148 => 'lyx',
+ 149 => 'lzh',
+ 150 => 'lzx',
+ 151 => 'frm',
+ 152 => 'mif',
+ 153 => 'wmd',
+ 154 => 'wmz',
+ 155 => 'com',
+ 156 => 'msi',
+ 157 => 'nc',
+ 158 => 'pac',
+ 159 => 'nwc',
+ 160 => 'o',
+ 161 => 'oza',
+ 162 => 'p7r',
+ 163 => 'crl',
+ 164 => 'pyc',
+ 165 => 'qgs',
+ 166 => 'qtl',
+ 167 => 'rpm',
+ 168 => 'rb',
+ 169 => 'sh',
+ 170 => 'shar',
+ 171 => 'swf',
+ 172 => 'scr',
+ 173 => 'sit',
+ 174 => 'sv4cpio',
+ 175 => 'sv4crc',
+ 176 => 'tar',
+ 177 => 'tcl',
+ 178 => 'gf',
+ 179 => 'pk',
+ 180 => 'texinfo',
+ 181 => 't',
+ 182 => 'man',
+ 183 => 'me',
+ 184 => 'ms',
+ 185 => 'ustar',
+ 186 => 'src',
+ 187 => 'wz',
+ 188 => 'crt',
+ 189 => 'xcf',
+ 190 => 'fig',
+ 191 => 'xpi',
+ 192 => 'amr',
+ 193 => 'awb',
+ 194 => 'amr',
+ 195 => 'awb',
+ 196 => 'axa',
+ 197 => 'au',
+ 198 => 'flac',
+ 199 => 'mid',
+ 200 => 'mpga',
+ 201 => 'm3u',
+ 202 => 'oga',
+ 203 => 'sid',
+ 204 => 'aif',
+ 205 => 'gsm',
+ 206 => 'm3u',
+ 207 => 'wma',
+ 208 => 'wax',
+ 209 => 'ra',
+ 210 => 'ra',
+ 211 => 'pls',
+ 212 => 'sd2',
+ 213 => 'wav',
+ 214 => 'alc',
+ 215 => 'cac',
+ 216 => 'csf',
+ 217 => 'cbin',
+ 218 => 'cdx',
+ 219 => 'cer',
+ 220 => 'c3d',
+ 221 => 'chm',
+ 222 => 'cif',
+ 223 => 'cmdf',
+ 224 => 'cml',
+ 225 => 'cpa',
+ 226 => 'bsd',
+ 227 => 'csml',
+ 228 => 'ctx',
+ 229 => 'cxf',
+ 230 => 'smi',
+ 231 => 'emb',
+ 232 => 'spc',
+ 233 => 'inp',
+ 234 => 'fch',
+ 235 => 'cub',
+ 236 => 'gau',
+ 237 => 'gal',
+ 238 => 'gcg',
+ 239 => 'gen',
+ 240 => 'hin',
+ 241 => 'istr',
+ 242 => 'jdx',
+ 243 => 'kin',
+ 244 => 'mcm',
+ 245 => 'mmd',
+ 246 => 'mol',
+ 247 => 'rd',
+ 248 => 'rxn',
+ 249 => 'sd',
+ 250 => 'tgf',
+ 251 => 'mif',
+ 252 => 'mcif',
+ 253 => 'mol2',
+ 254 => 'b',
+ 255 => 'gpt',
+ 256 => 'mop',
+ 257 => 'moo',
+ 258 => 'mvb',
+ 259 => 'asn',
+ 260 => 'prt',
+ 261 => 'val',
+ 262 => 'asn',
+ 263 => 'pdb',
+ 264 => 'ros',
+ 265 => 'sw',
+ 266 => 'vms',
+ 267 => 'vmd',
+ 268 => 'xtel',
+ 269 => 'xyz',
+ 270 => 'gif',
+ 271 => 'ief',
+ 272 => 'jpg',
+ 273 => 'pcx',
+ 274 => 'png',
+ 275 => 'svg',
+ 276 => 'tiff',
+ 277 => 'djvu',
+ 278 => 'wbmp',
+ 279 => 'cr2',
+ 280 => 'crw',
+ 281 => 'ras',
+ 282 => 'cdr',
+ 283 => 'pat',
+ 284 => 'cdt',
+ 285 => 'cpt',
+ 286 => 'erf',
+ 287 => 'ico',
+ 288 => 'art',
+ 289 => 'jng',
+ 290 => 'bmp',
+ 291 => 'nef',
+ 292 => 'orf',
+ 293 => 'psd',
+ 294 => 'pnm',
+ 295 => 'pbm',
+ 296 => 'pgm',
+ 297 => 'ppm',
+ 298 => 'rgb',
+ 299 => 'xbm',
+ 300 => 'xpm',
+ 301 => 'xwd',
+ 302 => 'eml',
+ 303 => 'igs',
+ 304 => 'msh',
+ 305 => 'wrl',
+ 306 => 'x3dv',
+ 307 => 'x3d',
+ 308 => 'x3db',
+ 309 => 'manifest',
+ 310 => 'ics',
+ 311 => 'css',
+ 312 => 'csv',
+ 313 => '323',
+ 314 => 'html',
+ 315 => 'uls',
+ 316 => 'mml',
+ 317 => 'asc',
+ 318 => 'rtx',
+ 319 => 'sct',
+ 320 => 'tm',
+ 321 => 'tsv',
+ 322 => 'jad',
+ 323 => 'wml',
+ 324 => 'wmls',
+ 325 => 'bib',
+ 326 => 'boo',
+ 327 => 'h',
+ 328 => 'c',
+ 329 => 'h',
+ 330 => 'htc',
+ 331 => 'csh',
+ 332 => 'c',
+ 333 => 'd',
+ 334 => 'diff',
+ 335 => 'hs',
+ 336 => 'java',
+ 337 => 'lhs',
+ 338 => 'moc',
+ 339 => 'p',
+ 340 => 'gcd',
+ 341 => 'pl',
+ 342 => 'py',
+ 343 => 'scala',
+ 344 => 'etx',
+ 345 => 'sh',
+ 346 => 'tcl',
+ 347 => 'tex',
+ 348 => 'vcs',
+ 349 => 'vcf',
+ 350 => '3gp',
+ 351 => 'axv',
+ 352 => 'dl',
+ 353 => 'dif',
+ 354 => 'fli',
+ 355 => 'gl',
+ 356 => 'mpeg',
+ 357 => 'mp4',
+ 358 => 'qt',
+ 359 => 'ogv',
+ 360 => 'mxu',
+ 361 => 'flv',
+ 362 => 'lsf',
+ 363 => 'mng',
+ 364 => 'asf',
+ 365 => 'wm',
+ 366 => 'wmv',
+ 367 => 'wmx',
+ 368 => 'wvx',
+ 369 => 'avi',
+ 370 => 'movie',
+ 371 => 'mpv',
+ 372 => 'ice',
+ 373 => 'sisx',
+ 374 => 'vrm',
+ 375 => 'jpeg',
+ );
+
+ /**
+ * @return MimeType
+ */
+ public static function wrap($id)
+ {
+ return new self($id);
+ }
+
+
+ /**
+ * Return MimeType object by mime-type string
+ * @param string $value
+ * @throws MissingElementException
+ * @return MimeType
+ */
+ public static function getByMimeType($value)
+ {
+ $list = static::getNameList();
+
+ $id = array_search(mb_strtolower($value), $list);
+ if ($id === false)
+ throw new MissingElementException('Can not find similar mime type "'.$value.'" !');
+
+ return new self($id);
+ }
+
+ /**
+ * Return MimeType object by extension without [dot] prefix
+ * @param string $value
+ * @throws MissingElementException
+ * @return MimeType
+ */
+ public static function getByExtension($value)
+ {
+ $list = static::getExtensionList();
+
+ $id = array_search(mb_strtolower($value), $list);
+ if ($id === false)
+ throw new MissingElementException('Can not find similar extension "'.$value.'" !');
+
+ return new self($id);
+ }
+
+
+ /**
+ * Extension list
+ * @return array
+ */
+ public static function getExtensionList()
+ {
+ return static::$extensions;
+ }
+
+ /**
+ * Return extension without [dot] prefix.
+ * @throws MissingElementException
+ * @return string
+ */
+ public function getExtension()
+ {
+ if(
+ isset( static::$extensions[$this->id] )
+ )
+ return static::$extensions[$this->id];
+
+ throw new MissingElementException(
+ 'Can not find "'.$this->id.'" in extensions map!'
+ );
+ }
+
+ /**
+ * Return Mime type string
+ * @return string
+ */
+ public function getMimeType()
+ {
+ return $this->getName();
+ }
+ }
+?>
\ No newline at end of file
diff --git a/main/Base/NamedTree.class.php b/main/Base/NamedTree.class.php
old mode 100644
new mode 100755
diff --git a/main/Base/Range.class.php b/main/Base/Range.class.php
old mode 100644
new mode 100755
diff --git a/main/Base/RequestType.class.php b/main/Base/RequestType.class.php
old mode 100644
new mode 100755
diff --git a/main/Base/SerializedObjectComparator.class.php b/main/Base/SerializedObjectComparator.class.php
old mode 100644
new mode 100755
diff --git a/main/Base/SingleRange.class.php b/main/Base/SingleRange.class.php
old mode 100644
new mode 100755
diff --git a/main/Base/StandardComparator.class.php b/main/Base/StandardComparator.class.php
old mode 100644
new mode 100755
diff --git a/main/Base/TempDirectory.class.php b/main/Base/TempDirectory.class.php
old mode 100644
new mode 100755
diff --git a/main/Base/TempFile.class.php b/main/Base/TempFile.class.php
old mode 100644
new mode 100755
diff --git a/main/Base/TimestampRange.class.php b/main/Base/TimestampRange.class.php
old mode 100644
new mode 100755
diff --git a/main/Base/TranslatedStore.class.php b/main/Base/TranslatedStore.class.php
new file mode 100644
index 0000000000..958bddc997
--- /dev/null
+++ b/main/Base/TranslatedStore.class.php
@@ -0,0 +1,32 @@
+toValue($string);
+ }
+}
\ No newline at end of file
diff --git a/main/Base/TransparentFile.class.php b/main/Base/TransparentFile.class.php
old mode 100644
new mode 100755
diff --git a/main/Charts/Google/BaseGoogleChartData.class.php b/main/Charts/Google/BaseGoogleChartData.class.php
old mode 100644
new mode 100755
diff --git a/main/Charts/Google/BaseGoogleChartDataEncoding.class.php b/main/Charts/Google/BaseGoogleChartDataEncoding.class.php
old mode 100644
new mode 100755
diff --git a/main/Charts/Google/BaseGoogleChartLabelStyleType.class.php b/main/Charts/Google/BaseGoogleChartLabelStyleType.class.php
old mode 100644
new mode 100755
diff --git a/main/Charts/Google/BaseGoogleChartParameter.class.php b/main/Charts/Google/BaseGoogleChartParameter.class.php
old mode 100644
new mode 100755
diff --git a/main/Charts/Google/BaseGoogleChartStyle.class.php b/main/Charts/Google/BaseGoogleChartStyle.class.php
old mode 100644
new mode 100755
diff --git a/main/Charts/Google/ChartLabelStyle.class.php b/main/Charts/Google/ChartLabelStyle.class.php
old mode 100644
new mode 100755
diff --git a/main/Charts/Google/ChartLineStyle.class.php b/main/Charts/Google/ChartLineStyle.class.php
old mode 100644
new mode 100755
diff --git a/main/Charts/Google/GoogleChart.class.php b/main/Charts/Google/GoogleChart.class.php
old mode 100644
new mode 100755
diff --git a/main/Charts/Google/GoogleChartAxis.class.php b/main/Charts/Google/GoogleChartAxis.class.php
old mode 100644
new mode 100755
diff --git a/main/Charts/Google/GoogleChartAxisCollection.class.php b/main/Charts/Google/GoogleChartAxisCollection.class.php
old mode 100644
new mode 100755
diff --git a/main/Charts/Google/GoogleChartAxisLabel.class.php b/main/Charts/Google/GoogleChartAxisLabel.class.php
old mode 100644
new mode 100755
diff --git a/main/Charts/Google/GoogleChartAxisType.class.php b/main/Charts/Google/GoogleChartAxisType.class.php
old mode 100644
new mode 100755
diff --git a/main/Charts/Google/GoogleChartColor.class.php b/main/Charts/Google/GoogleChartColor.class.php
old mode 100644
new mode 100755
diff --git a/main/Charts/Google/GoogleChartData.class.php b/main/Charts/Google/GoogleChartData.class.php
old mode 100644
new mode 100755
diff --git a/main/Charts/Google/GoogleChartDataEncoding.class.php b/main/Charts/Google/GoogleChartDataEncoding.class.php
old mode 100644
new mode 100755
diff --git a/main/Charts/Google/GoogleChartDataRange.class.php b/main/Charts/Google/GoogleChartDataRange.class.php
old mode 100644
new mode 100755
diff --git a/main/Charts/Google/GoogleChartDataScaling.class.php b/main/Charts/Google/GoogleChartDataScaling.class.php
old mode 100644
new mode 100755
diff --git a/main/Charts/Google/GoogleChartDataSet.class.php b/main/Charts/Google/GoogleChartDataSet.class.php
old mode 100644
new mode 100755
diff --git a/main/Charts/Google/GoogleChartDataSimpleEncoding.class.php b/main/Charts/Google/GoogleChartDataSimpleEncoding.class.php
old mode 100644
new mode 100755
diff --git a/main/Charts/Google/GoogleChartDataTextEncoding.class.php b/main/Charts/Google/GoogleChartDataTextEncoding.class.php
old mode 100644
new mode 100755
diff --git a/main/Charts/Google/GoogleChartGrid.class.php b/main/Charts/Google/GoogleChartGrid.class.php
old mode 100644
new mode 100755
diff --git a/main/Charts/Google/GoogleChartLabel.class.php b/main/Charts/Google/GoogleChartLabel.class.php
old mode 100644
new mode 100755
diff --git a/main/Charts/Google/GoogleChartLabelStyle.class.php b/main/Charts/Google/GoogleChartLabelStyle.class.php
old mode 100644
new mode 100755
diff --git a/main/Charts/Google/GoogleChartLabelStyleNumberType.class.php b/main/Charts/Google/GoogleChartLabelStyleNumberType.class.php
old mode 100644
new mode 100755
diff --git a/main/Charts/Google/GoogleChartLegend.class.php b/main/Charts/Google/GoogleChartLegend.class.php
old mode 100644
new mode 100755
diff --git a/main/Charts/Google/GoogleChartLegendPositionType.class.php b/main/Charts/Google/GoogleChartLegendPositionType.class.php
old mode 100644
new mode 100755
diff --git a/main/Charts/Google/GoogleChartLine.class.php b/main/Charts/Google/GoogleChartLine.class.php
old mode 100644
new mode 100755
diff --git a/main/Charts/Google/GoogleChartLineStyle.class.php b/main/Charts/Google/GoogleChartLineStyle.class.php
old mode 100644
new mode 100755
diff --git a/main/Charts/Google/GoogleChartPiece.class.php b/main/Charts/Google/GoogleChartPiece.class.php
old mode 100644
new mode 100755
diff --git a/main/Charts/Google/GoogleChartSize.class.php b/main/Charts/Google/GoogleChartSize.class.php
old mode 100644
new mode 100755
diff --git a/main/Charts/Google/GoogleChartSolidFill.class.php b/main/Charts/Google/GoogleChartSolidFill.class.php
old mode 100644
new mode 100755
diff --git a/main/Charts/Google/GoogleChartSolidFillCollection.class.php b/main/Charts/Google/GoogleChartSolidFillCollection.class.php
old mode 100644
new mode 100755
diff --git a/main/Charts/Google/GoogleChartSolidFillType.class.php b/main/Charts/Google/GoogleChartSolidFillType.class.php
old mode 100644
new mode 100755
diff --git a/main/Charts/Google/GoogleChartTitle.class.php b/main/Charts/Google/GoogleChartTitle.class.php
old mode 100644
new mode 100755
diff --git a/main/Charts/Google/GoogleChartType.class.php b/main/Charts/Google/GoogleChartType.class.php
old mode 100644
new mode 100755
diff --git a/main/Charts/Google/GoogleGridedLineChart.class.php b/main/Charts/Google/GoogleGridedLineChart.class.php
old mode 100644
new mode 100755
diff --git a/main/Charts/Google/GoogleLineChart.class.php b/main/Charts/Google/GoogleLineChart.class.php
old mode 100644
new mode 100755
diff --git a/main/Charts/Google/GoogleNormalizedLineChart.class.php b/main/Charts/Google/GoogleNormalizedLineChart.class.php
old mode 100644
new mode 100755
diff --git a/main/Charts/Google/GooglePieChart.class.php b/main/Charts/Google/GooglePieChart.class.php
old mode 100644
new mode 100755
diff --git a/main/Charts/Google/LabelStyleType.class.php b/main/Charts/Google/LabelStyleType.class.php
old mode 100644
new mode 100755
diff --git a/main/Criteria/Criteria.class.php b/main/Criteria/Criteria.class.php
old mode 100644
new mode 100755
index 08e97142ab..3666e7340d
--- a/main/Criteria/Criteria.class.php
+++ b/main/Criteria/Criteria.class.php
@@ -11,7 +11,7 @@
/**
* @see http://www.hibernate.org/hib_docs/v3/reference/en/html/querycriteria.html
- *
+ *
* @ingroup Criteria
**/
final class Criteria extends QueryIdentification
@@ -22,17 +22,23 @@ final class Criteria extends QueryIdentification
private $order = null;
private $strategy = null;
private $projection = null;
-
+
private $distinct = false;
-
+
private $limit = null;
private $offset = null;
-
+
private $collections = array();
-
+
// dao-like behaviour: will throw ObjectNotFoundException when 'false'
private $silent = true;
-
+
+ // Кастомный запрос
+ private $selectQuery = null;
+
+ // additional joins for cases
+ private $customJoins = array();
+
/**
* @return Criteria
**/
@@ -40,19 +46,19 @@ public static function create(/* ProtoDAO */ $dao = null)
{
return new self($dao);
}
-
+
public function __construct(/* ProtoDAO */ $dao = null)
{
if ($dao)
Assert::isTrue($dao instanceof ProtoDAO);
-
+
$this->dao = $dao;
$this->logic = Expression::andBlock();
$this->order = new OrderChain();
$this->strategy = FetchStrategy::join();
$this->projection = Projection::chain();
}
-
+
public function __clone()
{
$this->logic = clone $this->logic;
@@ -60,25 +66,25 @@ public function __clone()
$this->strategy = clone $this->strategy;
$this->projection = clone $this->projection;
}
-
+
public function __sleep()
{
$this->daoClass =
$this->getDao()
? get_class($this->dao)
: null;
-
+
$vars = get_object_vars($this);
unset($vars['dao']);
return array_keys($vars);
}
-
+
public function __wakeup()
{
if ($this->daoClass)
$this->dao = Singleton::getInstance($this->daoClass);
}
-
+
/**
* @return ProtoDAO
**/
@@ -86,25 +92,29 @@ public function getDao()
{
return $this->dao;
}
-
+
/**
* @return Criteria
**/
public function setDao(ProtoDAO $dao)
{
$this->dao = $dao;
-
+
return $this;
}
-
+
+ /**
+ * @return ProtoDAO
+ * @throws WrongStateException
+ */
public function checkAndGetDao()
{
if (!$this->dao)
throw new WrongStateException('You forgot to set dao');
-
+
return $this->dao;
}
-
+
/**
* @return LogicalChain
**/
@@ -112,17 +122,37 @@ public function getLogic()
{
return $this->logic;
}
-
+
+ /**
+ * @param LogicalChain $logic
+ * @return Criteria
+ */
+ public function setLogic(LogicalChain $logic) {
+ $this->logic = $logic;
+
+ return $this;
+ }
+
+ /**
+ * @return Criteria
+ */
+ public function dropLogic()
+ {
+ $this->logic = Expression::andBlock();
+
+ return $this;
+ }
+
/**
* @return Criteria
**/
public function add(LogicalObject $logic)
{
$this->logic->expAnd($logic);
-
+
return $this;
}
-
+
/**
* @return OrderChain
**/
@@ -130,7 +160,17 @@ public function getOrder()
{
return $this->order;
}
-
+
+ /**
+ * @param OrderChain $order
+ * @return Criteria
+ */
+ public function setOrder(OrderChain $order) {
+ $this->order = $order;
+
+ return $this;
+ }
+
/**
* @return Criteria
**/
@@ -138,12 +178,12 @@ public function addOrder(/* MapableObject */ $order)
{
if (!$order instanceof MappableObject)
$order = new OrderBy($order);
-
+
$this->order->add($order);
-
+
return $this;
}
-
+
/**
* @return Criteria
**/
@@ -151,52 +191,52 @@ public function prependOrder(/* MapableObject */ $order)
{
if (!$order instanceof MappableObject)
$order = new OrderBy($order);
-
+
$this->order->prepend($order);
-
+
return $this;
}
-
+
/**
* @return Criteria
**/
public function dropOrder()
{
$this->order = new OrderChain();
-
+
return $this;
}
-
+
public function getLimit()
{
return $this->limit;
}
-
+
/**
* @return Criteria
**/
public function setLimit($limit)
{
$this->limit = $limit;
-
+
return $this;
}
-
+
public function getOffset()
{
return $this->offset;
}
-
+
/**
* @return Criteria
**/
public function setOffset($offset)
{
$this->offset = $offset;
-
+
return $this;
}
-
+
/**
* @return FetchStrategy
**/
@@ -204,17 +244,17 @@ public function getFetchStrategy()
{
return $this->strategy;
}
-
+
/**
* @return Criteria
**/
public function setFetchStrategy(FetchStrategy $strategy)
{
$this->strategy = $strategy;
-
+
return $this;
}
-
+
/**
* @return Criteria
**/
@@ -224,10 +264,10 @@ public function setProjection(ObjectProjection $chain)
$this->projection = $chain;
else
$this->projection = Projection::chain()->add($chain);
-
+
return $this;
}
-
+
/**
* @return Criteria
**/
@@ -238,10 +278,10 @@ public function addProjection(ObjectProjection $projection)
|| !$projection->isEmpty()
)
$this->projection->add($projection);
-
+
return $this;
}
-
+
/**
* @return ProjectionChain
**/
@@ -249,49 +289,49 @@ public function getProjection()
{
return $this->projection;
}
-
+
/**
* @return Criteria
**/
public function dropProjection()
{
$this->projection = Projection::chain();
-
+
return $this;
}
-
+
/**
* @return Criteria
**/
public function setDistinct($orly = true)
{
$this->distinct = ($orly === true);
-
+
return $this;
}
-
+
public function isDistinct()
{
return $this->distinct;
}
-
+
public function isSilent()
{
return $this->silent;
}
-
+
/**
* @return Criteria
**/
public function setSilent($silent)
{
Assert::isBoolean($silent);
-
+
$this->silent = $silent;
-
+
return $this;
}
-
+
/**
* @return Criteria
**/
@@ -306,7 +346,7 @@ public function fetchCollection(
($criteria === null)
|| ($criteria instanceof Criteria)
);
-
+
$this->collections[$path]['lazy'] = $lazy;
$this->collections[$path]['criteria'] = $criteria;
$this->collections[$path]['propertyPath']
@@ -314,68 +354,74 @@ public function fetchCollection(
$this->checkAndGetDao()->getObjectName(),
$path
);
-
+
return $this;
}
-
+
public function get()
{
try {
- $list = array(
- $this->checkAndGetDao()->
- getByQuery($this->toSelectQuery())
- );
+ $dao = $this->checkAndGetDao();
+ $list =
+ $dao instanceof NoSqlDAO
+ ? $dao->getListByCriteria($this)
+ : $dao->getListByQuery($this->toSelectQuery());
} catch (ObjectNotFoundException $e) {
if (!$this->isSilent())
throw $e;
-
+
return null;
}
-
+
if (!$this->collections || !$list)
return reset($list);
-
+
$list =
$this->checkAndGetDao()->
fetchCollections($this->collections, $list);
-
+
return reset($list);
}
-
+
public function getList()
{
try {
+ $dao = $this->checkAndGetDao();
$list =
- $this->checkAndGetDao()->
- getListByQuery($this->toSelectQuery());
-
+ $dao instanceof NoSqlDAO
+ ? $dao->getListByCriteria($this)
+ : $dao->getListByQuery($this->toSelectQuery());
} catch (ObjectNotFoundException $e) {
if (!$this->isSilent())
throw $e;
-
+
return array();
}
-
+
if (!$this->collections || !$list)
return $list;
-
+
return
$this->checkAndGetDao()->
fetchCollections($this->collections, $list);
}
-
+
/**
- * @return QueryResult
+ * @return QueryResult|NoSqlResult
**/
public function getResult()
{
- $result =
- $this->checkAndGetDao()->
- getQueryResult($this->toSelectQuery());
-
+ $dao = $this->checkAndGetDao();
+ if ($dao instanceof NoSqlDAO) {
+ /** @var $dao NoSqlDAO */
+ $result = $dao->getNoSqlResult($this);
+ } else {
+ $result = $dao->getQueryResult($this->toSelectQuery());
+ }
+
if (!$this->collections || !$result->getCount())
return $result;
-
+
return $result->setList(
$this->checkAndGetDao()->fetchCollections(
$this->collections,
@@ -383,61 +429,68 @@ public function getResult()
)
);
}
-
+
public function getCustom($index = null)
{
try {
$result =
$this->checkAndGetDao()->getCustom($this->toSelectQuery());
-
+
if ($index) {
if (array_key_exists($index, $result))
return $result[$index];
-
+
throw new MissingElementException(
'No such key: "'.$index.'" in result set.'
);
}
-
+
return $result;
} catch (ObjectNotFoundException $e) {
if (!$this->isSilent())
throw $e;
-
+
return null;
}
}
-
+
public function getCustomList()
{
try {
return
$this->checkAndGetDao()->
getCustomList($this->toSelectQuery());
-
+
} catch (ObjectNotFoundException $e) {
if (!$this->isSilent())
throw $e;
-
+
return array();
}
}
-
+
+ /**
+ * @return Cursor
+ */
+ public function getCursor() {
+ return Cursor::create($this->getDao(), $this->toSelectQuery());
+ }
+
public function getPropertyList()
{
try {
return
$this->checkAndGetDao()->
getCustomRowList($this->toSelectQuery());
-
+
} catch (ObjectNotFoundException $e) {
if (!$this->isSilent())
throw $e;
-
+
return array();
}
}
-
+
public function toString()
{
return $this->toDialectString(
@@ -446,17 +499,21 @@ public function toString()
: ImaginaryDialect::me()
);
}
-
+
public function toDialectString(Dialect $dialect)
{
return $this->toSelectQuery()->toDialectString($dialect);
}
-
+
/**
* @return SelectQuery
**/
public function toSelectQuery()
{
+ if( !is_null($this->selectQuery) ) {
+ return $this->selectQuery;
+ }
+
if (!$this->projection->isEmpty()) {
$query =
$this->getProjection()->process(
@@ -466,13 +523,15 @@ public function toSelectQuery()
);
} else
$query = $this->checkAndGetDao()->makeSelectHead();
-
+
+ $query->setCriteria($this);
+
if ($this->distinct)
$query->distinct();
-
+
return $this->fillSelectQuery($query);
}
-
+
/**
* @return SelectQuery
**/
@@ -480,23 +539,23 @@ public function fillSelectQuery(SelectQuery $query)
{
$query->
limit($this->limit, $this->offset);
-
+
if ($this->distinct)
$query->distinct();
-
+
if ($this->logic->getSize()) {
$query->
andWhere(
$this->logic->toMapped($this->checkAndGetDao(), $query)
);
}
-
+
if ($this->order) {
$query->setOrderChain(
$this->order->toMapped($this->checkAndGetDao(), $query)
);
}
-
+
if (
$this->projection->isEmpty()
&& (
@@ -510,22 +569,37 @@ public function fillSelectQuery(SelectQuery $query)
true
);
}
-
+
+ if ($this->customJoins) {
+ foreach ($this->customJoins as $join) {
+ $query->customJoin($join);
+ }
+ }
+
return $query;
}
-
+
/**
* @return Criteria
**/
public function dropProjectionByType(/* array */ $dropTypes)
{
Assert::isInstance($this->projection, 'ProjectionChain');
-
+
$this->projection->dropByType($dropTypes);
-
+
+ return $this;
+ }
+
+ /**
+ * @param SelectQuery $query
+ * @return Criteria
+ **/
+ public function setSelectQuery(SelectQuery $query) {
+ $this->selectQuery = $query;
return $this;
}
-
+
private function joinProperties(
SelectQuery $query,
ProtoDAO $parentDao,
@@ -535,7 +609,7 @@ private function joinProperties(
)
{
$proto = call_user_func(array($parentDao->getObjectName(), 'proto'));
-
+
foreach ($proto->getPropertyList() as $property) {
if (
($property instanceof LightMetaProperty)
@@ -558,6 +632,14 @@ private function joinProperties(
is_subclass_of(
$property->getClassName(),
'Enumeration'
+ ) ||
+ is_subclass_of(
+ $property->getClassName(),
+ 'Enum'
+ ) ||
+ is_subclass_of(
+ $property->getClassName(),
+ 'Registry'
)
) {
// field already added by makeSelectHead
@@ -566,7 +648,7 @@ private function joinProperties(
$proto = call_user_func(
array($property->getClassName(), 'proto')
);
-
+
foreach ($proto->getPropertyList() as $innerProperty)
$query->get(
new DBField(
@@ -574,25 +656,25 @@ private function joinProperties(
$parentTable
)
);
-
+
continue;
}
-
+
$propertyDao = call_user_func(
array($property->getClassName(), 'dao')
);
-
+
// add's custom dao's injection possibility
if (!$propertyDao instanceof ProtoDAO)
continue;
-
+
$tableAlias = $propertyDao->getJoinName(
$property->getColumnName(),
$prefix
);
-
+
$fields = $propertyDao->getFields();
-
+
if (!$query->hasJoinedTable($tableAlias)) {
$logic =
Expression::eq(
@@ -600,19 +682,19 @@ private function joinProperties(
$property->getColumnName(),
$parentTable
),
-
+
DBField::create(
$propertyDao->getIdName(),
$tableAlias
)
);
-
+
if ($property->isRequired() && $parentRequired)
$query->join($propertyDao->getTable(), $logic, $tableAlias);
else
$query->leftJoin($propertyDao->getTable(), $logic, $tableAlias);
}
-
+
foreach ($fields as $field) {
$query->get(
new DBField($field, $tableAlias),
@@ -620,7 +702,7 @@ private function joinProperties(
.$field
);
}
-
+
$this->joinProperties(
$query,
$propertyDao,
@@ -631,7 +713,7 @@ private function joinProperties(
}
}
}
-
+
/**
* @return AbstractProtoClass
**/
@@ -642,5 +724,22 @@ private function getProto()
array($this->checkAndGetDao()->getObjectName(), 'proto')
);
}
+
+ /**
+ * @param SQLBaseJoin $customJoin
+ * @return $this
+ */
+ public function addCustomJoin(SQLBaseJoin $customJoin) {
+ $this->customJoins []= $customJoin;
+ return $this;
+ }
+
+ /**
+ * @return $this
+ */
+ public function dropCustomJoins() {
+ $this->customJoins = array();
+ return $this;
+ }
}
?>
\ No newline at end of file
diff --git a/main/Criteria/Cursor.class.php b/main/Criteria/Cursor.class.php
new file mode 100644
index 0000000000..7727384742
--- /dev/null
+++ b/main/Criteria/Cursor.class.php
@@ -0,0 +1,203 @@
+
+ * @date 2013.04.25
+ */
+final class Cursor implements Iterator {
+
+ /** @var ProtoDAO */
+ protected $dao = null;
+
+ /** @var SelectQuery */
+ protected $selectQuery = null;
+
+ /** @var DB */
+ protected $db = null;
+
+ /** @var string */
+ protected $cursorName = null;
+
+ /** @var array */
+ protected $buffer = array();
+
+ /** @var int */
+ protected $batchSize = 250;
+
+ /** @var bool */
+ protected $iterateObjects = null;
+
+ /** @var int */
+ protected $iteratorPosition = 0;
+
+ /** @var mixed */
+ protected $iteratorCurrent = null;
+
+ public static function create(ProtoDAO $dao, SelectQuery $query = null) {
+ return new self($dao, $query);
+ }
+
+ public function __construct(ProtoDAO $dao, SelectQuery $query = null) {
+ if ($query)
+ Assert::isTrue($query instanceof SelectQuery);
+
+ $this->dao = $dao;
+ $this->db = DBPool::getByDao($this->dao);
+ $this->selectQuery = $query;
+
+ $this->openTransaction();
+ $this->declareCursor();
+ }
+
+ function __destruct() {
+ if( $this->db->inTransaction() && is_resource($this->db->getLink()) ) {
+ $this->closeCursor();
+ $this->closeTransaction();
+ }
+ }
+
+ public function __clone()
+ {
+ $this->dao = clone $this->dao;
+ $this->selectQuery = clone $this->selectQuery;
+ }
+
+ /**
+ * @return Cursor
+ */
+ public function asObjects() {
+ $this->iterateObjects = true;
+ return $this;
+ }
+
+ /**
+ * @return Cursor
+ */
+ public function asRows() {
+ $this->iterateObjects = false;
+ return $this;
+ }
+
+ public function setBatchSize($size) {
+ if( Assert::checkInteger($size) && $size>0 ) {
+ $this->batchSize = $size;
+ }
+ return $this;
+ }
+
+ /**
+ * @param bool $strict
+ * @throws ObjectNotFoundException
+ * @return Prototyped|bool
+ */
+ public function getNext($strict=false) {
+ $row = $this->fetchRow();
+ if( !$row ) {
+ if( $strict ) {
+ throw new ObjectNotFoundException();
+ }
+ return false;
+ }
+ return $this->dao->getProtoClass()->makeOnlyObject($this->dao->getObjectName(), $row);
+ }
+
+ /**
+ * @param bool $strict
+ * @throws ObjectNotFoundException
+ * @return array|bool
+ */
+ public function getNextRow($strict=false) {
+ $row = $this->fetchRow();
+ if( !$row ) {
+ if( $strict ) {
+ throw new ObjectNotFoundException();
+ }
+ return false;
+ }
+ return $row;
+ }
+
+ public function current() {
+ return $this->iteratorCurrent;
+ }
+
+ public function key() {
+ return $this->iteratorPosition;
+ }
+
+ public function next() {
+ $this->iteratorPosition++;
+ }
+
+ public function valid() {
+ $this->iteratorCurrent = $this->iterateObjects ? $this->getNext() : $this->getNextRow();
+ return $this->iteratorCurrent===false ? false : true;
+ }
+
+ public function rewind() {
+ if( is_null($this->iterateObjects) ) {
+ throw new WrongStateException('Type of iterating objects is not defined, use asObjects() or asRows()');
+ }
+ $this->iteratorPosition = 0;
+ }
+
+ public function close() {
+ $this->closeCursor();
+ $this->closeTransaction();
+ $this->cursorName = null;
+ }
+
+
+ /**
+ * @return SelectQuery
+ */
+ protected function getSelectQuery() {
+ if( is_null($this->selectQuery) ) {
+ $this->selectQuery = $this->dao->makeSelectHead();
+ }
+ return $this->selectQuery;
+
+ }
+
+ /**
+ * @return string
+ */
+ protected function getCursorName() {
+ if( is_null($this->cursorName) ) {
+ $this->cursorName = 'cursor_'.dechex(crc32(time().$this->dao->getTable()));
+ }
+ return $this->cursorName;
+ }
+
+ protected function openTransaction() {
+ $this->db->begin();
+ }
+
+ protected function declareCursor() {
+ $queryDeclare = 'DECLARE '.$this->getCursorName().' CURSOR FOR '.$this->getSelectQuery()->toDialectString($this->db->getDialect());
+ $this->db->queryRaw($queryDeclare);
+ }
+
+ protected function fetchRow() {
+ if( empty($this->buffer) ) {
+ $resource = $this->db->queryRaw('FETCH FORWARD 250 FROM '.$this->getCursorName());
+ $this->buffer = pg_fetch_all($resource);
+ }
+ if( !$this->buffer ) {
+ return false;
+ }
+ return array_shift($this->buffer);
+ }
+
+ protected function closeCursor() {
+ $queryOpen = 'CLOSE '.$this->getCursorName();
+ $this->db->queryRaw($queryOpen);
+ }
+
+ protected function closeTransaction() {
+ $this->db->commit();
+ }
+
+ final private function __sleep() {/* restless class */}
+ final private function __wakeup() {/* restless class */}
+}
\ No newline at end of file
diff --git a/main/Criteria/FetchStrategy.class.php b/main/Criteria/FetchStrategy.class.php
old mode 100644
new mode 100755
diff --git a/main/Criteria/Projection.class.php b/main/Criteria/Projection.class.php
old mode 100644
new mode 100755
index eeff26453f..92f12eaf87
--- a/main/Criteria/Projection.class.php
+++ b/main/Criteria/Projection.class.php
@@ -119,5 +119,13 @@ public static function clazz($className)
{
return new ClassProjection($className);
}
+
+ /**
+ * @return CrippleClassProjection
+ **/
+ public static function crippleClass($className)
+ {
+ return new CrippleClassProjection($className);
+ }
}
?>
\ No newline at end of file
diff --git a/main/Criteria/Projections/AggregateProjection.class.php b/main/Criteria/Projections/AggregateProjection.class.php
old mode 100644
new mode 100755
diff --git a/main/Criteria/Projections/AverageNumberProjection.class.php b/main/Criteria/Projections/AverageNumberProjection.class.php
old mode 100644
new mode 100755
diff --git a/main/Criteria/Projections/BaseProjection.class.php b/main/Criteria/Projections/BaseProjection.class.php
old mode 100644
new mode 100755
index dd3025f3a9..b7f9ff96eb
--- a/main/Criteria/Projections/BaseProjection.class.php
+++ b/main/Criteria/Projections/BaseProjection.class.php
@@ -22,10 +22,20 @@ public function __construct($propertyName = null, $alias = null)
$this->property = $propertyName;
$this->alias = $alias;
}
-
+
public function getAlias()
{
return $this->alias;
}
+
+ public function getPropertyName()
+ {
+ return $this->property;
+ }
+
+ public function setPropertyName($propertyName = null)
+ {
+ $this->property = $propertyName;
+ }
}
?>
\ No newline at end of file
diff --git a/main/Criteria/Projections/ClassProjection.class.php b/main/Criteria/Projections/ClassProjection.class.php
old mode 100644
new mode 100755
diff --git a/main/Criteria/Projections/CountProjection.class.php b/main/Criteria/Projections/CountProjection.class.php
old mode 100644
new mode 100755
diff --git a/main/Criteria/Projections/CrippleClassProjection.class.php b/main/Criteria/Projections/CrippleClassProjection.class.php
new file mode 100644
index 0000000000..a98cbda369
--- /dev/null
+++ b/main/Criteria/Projections/CrippleClassProjection.class.php
@@ -0,0 +1,29 @@
+
+ * @author Alex Gorbylev
+ * @date 2012.12.06
+ */
+class CrippleClassProjection extends ClassProjection {
+
+ private $excludedFields = array();
+
+ public function excludeField($field) {
+ Assert::isString($field);
+ $this->excludedFields[$field] = $field;
+ return $this;
+ }
+
+ /* void */
+ protected function subProcess(JoinCapableQuery $query, DBField $field) {
+ // if need to exclude change field to NULL
+ if( array_key_exists($field->getField(), $this->excludedFields) ) {
+ $nullField = DBRaw::create('NULL');
+ $query->get($nullField, $field->getField());
+ } else {
+ $query->get($field);
+ }
+ }
+
+
+}
diff --git a/main/Criteria/Projections/DistinctCountProjection.class.php b/main/Criteria/Projections/DistinctCountProjection.class.php
old mode 100644
new mode 100755
diff --git a/main/Criteria/Projections/GroupByClassProjection.class.php b/main/Criteria/Projections/GroupByClassProjection.class.php
old mode 100644
new mode 100755
diff --git a/main/Criteria/Projections/GroupByPropertyProjection.class.php b/main/Criteria/Projections/GroupByPropertyProjection.class.php
old mode 100644
new mode 100755
diff --git a/main/Criteria/Projections/HavingProjection.class.php b/main/Criteria/Projections/HavingProjection.class.php
old mode 100644
new mode 100755
diff --git a/main/Criteria/Projections/MappableObjectProjection.class.php b/main/Criteria/Projections/MappableObjectProjection.class.php
old mode 100644
new mode 100755
diff --git a/main/Criteria/Projections/MaximalNumberProjection.class.php b/main/Criteria/Projections/MaximalNumberProjection.class.php
old mode 100644
new mode 100755
diff --git a/main/Criteria/Projections/MinimalNumberProjection.class.php b/main/Criteria/Projections/MinimalNumberProjection.class.php
old mode 100644
new mode 100755
diff --git a/main/Criteria/Projections/ObjectProjection.class.php b/main/Criteria/Projections/ObjectProjection.class.php
old mode 100644
new mode 100755
diff --git a/main/Criteria/Projections/ProjectionChain.class.php b/main/Criteria/Projections/ProjectionChain.class.php
old mode 100644
new mode 100755
index b9c0c8e4c3..1d225a1ac9
--- a/main/Criteria/Projections/ProjectionChain.class.php
+++ b/main/Criteria/Projections/ProjectionChain.class.php
@@ -31,6 +31,19 @@ public function add(ObjectProjection $projection, $name = null)
return $this;
}
+
+ /**
+ * @return array
+ */
+ public function getList()
+ {
+ return $this->list;
+ }
+
+ public function dropChain()
+ {
+ $this->list = array();
+ }
/**
* @return JoinCapableQuery
@@ -39,7 +52,7 @@ public function process(Criteria $criteria, JoinCapableQuery $query)
{
foreach ($this->list as $projection)
$projection->process($criteria, $query);
-
+
return $query;
}
diff --git a/main/Criteria/Projections/PropertyProjection.class.php b/main/Criteria/Projections/PropertyProjection.class.php
old mode 100644
new mode 100755
diff --git a/main/Criteria/Projections/RowCountProjection.class.php b/main/Criteria/Projections/RowCountProjection.class.php
old mode 100644
new mode 100755
diff --git a/main/Criteria/Projections/SumProjection.class.php b/main/Criteria/Projections/SumProjection.class.php
old mode 100644
new mode 100755
diff --git a/main/Criteria/PropertyPath.class.php b/main/Criteria/PropertyPath.class.php
old mode 100644
new mode 100755
diff --git a/main/Crypto/Base62Utils.class.php b/main/Crypto/Base62Utils.class.php
old mode 100644
new mode 100755
diff --git a/main/Crypto/Crypter.class.php b/main/Crypto/Crypter.class.php
old mode 100644
new mode 100755
diff --git a/main/Crypto/CryptoFunctions.class.php b/main/Crypto/CryptoFunctions.class.php
old mode 100644
new mode 100755
diff --git a/main/Crypto/DiffieHellmanKeyPair.class.php b/main/Crypto/DiffieHellmanKeyPair.class.php
old mode 100644
new mode 100755
diff --git a/main/Crypto/DiffieHellmanParameters.class.php b/main/Crypto/DiffieHellmanParameters.class.php
old mode 100644
new mode 100755
diff --git a/main/Crypto/KeyPair.class.php b/main/Crypto/KeyPair.class.php
old mode 100644
new mode 100755
diff --git a/main/DAOs/BaseDAO.class.php b/main/DAOs/BaseDAO.class.php
old mode 100644
new mode 100755
index 8ddbeb4cef..c976a8f57f
--- a/main/DAOs/BaseDAO.class.php
+++ b/main/DAOs/BaseDAO.class.php
@@ -53,6 +53,8 @@ public function dropByIds(array $ids);
public function uncacheById($id);
public function uncacheByIds($ids);
public function uncacheLists();
+ public function uncacheByQuery(SelectQuery $query);
+ public function uncacheItems();
//@}
}
?>
\ No newline at end of file
diff --git a/main/DAOs/DAOConnected.class.php b/main/DAOs/DAOConnected.class.php
old mode 100644
new mode 100755
diff --git a/main/DAOs/EmptyDAO.class.php b/main/DAOs/EmptyDAO.class.php
new file mode 100644
index 0000000000..7a420942aa
--- /dev/null
+++ b/main/DAOs/EmptyDAO.class.php
@@ -0,0 +1,18 @@
+
+ * @date 2013.04.19
+ */
+
+abstract class EmptyDAO extends Singleton {
+
+ public function getIdName() {
+ return 'id';
+ }
+
+ public function getById($id) {
+ throw new UnimplementedFeatureException;
+ }
+
+}
\ No newline at end of file
diff --git a/main/DAOs/FullTextDAO.class.php b/main/DAOs/FullTextDAO.class.php
old mode 100644
new mode 100755
diff --git a/main/DAOs/GenericDAO.class.php b/main/DAOs/GenericDAO.class.php
old mode 100644
new mode 100755
index ab0cd44dd5..ee5c0d5b92
--- a/main/DAOs/GenericDAO.class.php
+++ b/main/DAOs/GenericDAO.class.php
@@ -11,16 +11,18 @@
/**
* Basis of all DAO's.
- *
+ *
* @ingroup DAOs
**/
abstract class GenericDAO extends Singleton implements BaseDAO
{
private $identityMap = array();
-
+
+ private $triggersAllowed = true;
+
abstract public function getTable();
abstract public function getObjectName();
-
+
public function makeObject($array, $prefix = null)
{
if (
@@ -33,16 +35,16 @@ public function makeObject($array, $prefix = null)
$this->getProtoClass()->skipObjectPrefetching(
$this->identityMap[$array[$idName]]
);
-
+
return $this->identityMap[$array[$idName]];
}
-
+
return
$this->completeObject(
$this->makeOnlyObject($array, $prefix)
);
}
-
+
public function makeOnlyObject($array, $prefix = null)
{
// adding incomplete object to identity map
@@ -53,7 +55,7 @@ public function makeOnlyObject($array, $prefix = null)
)
);
}
-
+
public function completeObject(Identifiable $object)
{
return $this->getProtoClass()->completeObject(
@@ -62,82 +64,82 @@ public function completeObject(Identifiable $object)
$this->addObjectToMap($object)
);
}
-
+
/**
* Returns link name which is used to get actual DB-link from DBPool,
* returning null by default for single-source projects.
- *
+ *
* @see DBPool
**/
public function getLinkName()
{
return null;
}
-
+
public function getIdName()
{
return 'id';
}
-
+
public function getSequence()
{
return $this->getTable().'_id';
}
-
+
/**
* @return AbstractProtoClass
**/
public function getProtoClass()
{
static $protos = array();
-
+
if (!isset($protos[$className = $this->getObjectName()]))
$protos[$className] = call_user_func(array($className, 'proto'));
-
+
return $protos[$className];
}
-
+
public function getMapping()
{
return $this->getProtoClass()->getMapping();
}
-
+
public function getFields()
{
static $fields = array();
-
+
$className = $this->getObjectName();
-
+
if (!isset($fields[$className])) {
$fields[$className] = array_values($this->getMapping());
}
-
+
return $fields[$className];
}
-
+
/**
* @return SelectQuery
**/
public function makeSelectHead()
{
static $selectHead = array();
-
+
if (!isset($selectHead[$className = $this->getObjectName()])) {
$table = $this->getTable();
-
+
$object =
OSQL::select()->
from($table);
-
+
foreach ($this->getFields() as $field)
$object->get(new DBField($field, $table));
-
+
$selectHead[$className] = $object;
}
-
+
return clone $selectHead[$className];
}
-
+
/**
* @return SelectQuery
**/
@@ -150,22 +152,22 @@ public function makeTotalCountQuery()
)->
from($this->getTable());
}
-
+
/// boring delegates
//@{
public function getById($id, $expires = Cache::EXPIRES_MEDIUM)
{
Assert::isScalar($id);
Assert::isNotEmpty($id);
-
+
if (isset($this->identityMap[$id]))
return $this->identityMap[$id];
-
+
return $this->addObjectToMap(
Cache::worker($this)->getById($id, $expires)
);
}
-
+
public function getByLogic(
LogicalObject $logic, $expires = Cache::DO_NOT_CACHE
)
@@ -174,7 +176,7 @@ public function getByLogic(
Cache::worker($this)->getByLogic($logic, $expires)
);
}
-
+
public function getByQuery(
SelectQuery $query, $expires = Cache::DO_NOT_CACHE
)
@@ -183,20 +185,20 @@ public function getByQuery(
Cache::worker($this)->getByQuery($query, $expires)
);
}
-
+
public function getCustom(
SelectQuery $query, $expires = Cache::DO_NOT_CACHE
)
{
return Cache::worker($this)->getCustom($query, $expires);
}
-
+
public function getListByIds(
array $ids, $expires = Cache::EXPIRES_MEDIUM
)
{
$mapped = $remain = array();
-
+
foreach ($ids as $id) {
if (isset($this->identityMap[$id])) {
$mapped[] = $this->identityMap[$id];
@@ -204,18 +206,18 @@ public function getListByIds(
$remain[] = $id;
}
}
-
+
if ($remain) {
$list = $this->addObjectListToMap(
Cache::worker($this)->getListByIds($remain, $expires)
);
-
+
$mapped = array_merge($mapped, $list);
}
-
+
return ArrayUtils::regularizeList($ids, $mapped);
}
-
+
public function getListByQuery(
SelectQuery $query, $expires = Cache::DO_NOT_CACHE
)
@@ -224,7 +226,7 @@ public function getListByQuery(
Cache::worker($this)->getListByQuery($query, $expires)
);
}
-
+
public function getListByLogic(
LogicalObject $logic, $expires = Cache::DO_NOT_CACHE
)
@@ -233,102 +235,129 @@ public function getListByLogic(
Cache::worker($this)->getListByLogic($logic, $expires)
);
}
-
+
public function getPlainList($expires = Cache::EXPIRES_MEDIUM)
{
return $this->addObjectListToMap(
Cache::worker($this)->getPlainList($expires)
);
}
-
+
public function getTotalCount($expires = Cache::DO_NOT_CACHE)
{
return Cache::worker($this)->getTotalCount($expires);
}
-
+
public function getCustomList(
SelectQuery $query, $expires = Cache::DO_NOT_CACHE
)
{
return Cache::worker($this)->getCustomList($query, $expires);
}
-
+
public function getCustomRowList(
SelectQuery $query, $expires = Cache::DO_NOT_CACHE
)
{
return Cache::worker($this)->getCustomRowList($query, $expires);
}
-
+
public function getQueryResult(
SelectQuery $query, $expires = Cache::DO_NOT_CACHE
)
{
return Cache::worker($this)->getQueryResult($query, $expires);
}
-
+
public function drop(Identifiable $object)
{
$this->checkObjectType($object);
-
+
return $this->dropById($object->getId());
}
-
+
public function dropById($id)
{
+ call_user_func($this->prepareTrigger($id, 'onBeforeDrop'));
+ $after = $this->prepareTrigger($id, 'onAfterDrop');
+
unset($this->identityMap[$id]);
-
+
$count = Cache::worker($this)->dropById($id);
-
+
+ call_user_func($after);
+
if (1 != $count)
throw new WrongStateException('no object were dropped');
-
+
return $count;
}
-
+
public function dropByIds(array $ids)
{
+ call_user_func($this->prepareTrigger($ids, 'onBeforeDrop'));
+ $after = $this->prepareTrigger($ids, 'onAfterDrop');
+
foreach ($ids as $id)
unset($this->identityMap[$id]);
-
+
$count = Cache::worker($this)->dropByIds($ids);
-
+
+ call_user_func($after);
+
if ($count != count($ids))
throw new WrongStateException('not all objects were dropped');
-
+
return $count;
}
-
+
public function uncacheById($id)
{
unset($this->identityMap[$id]);
-
+
return Cache::worker($this)->uncacheById($id);
}
-
+
public function uncacheByIds($ids)
{
foreach ($ids as $id)
unset($this->identityMap[$id]);
-
+
return Cache::worker($this)->uncacheByIds($ids);
}
-
+
public function uncacheLists()
{
$this->dropIdentityMap();
-
+
return Cache::worker($this)->uncacheLists();
}
- //@}
-
+
+ public function uncacheByQuery(SelectQuery $query) {
+ try {
+ $item = Cache::worker($this)->getByQuery($query);
+ if ($item instanceof Identifiable && isset($this->identityMap[$item->getId()])) {
+ unset($this->identityMap[$item->getId()]);
+ }
+ } catch (ObjectNotFoundException $e) {}
+ return Cache::worker($this)->uncacheByQuery($query);
+ }
+
+ public function uncacheItems() {
+
+ $this->dropIdentityMap();
+
+ return Cache::worker($this)->uncacheItems();
+ }
+ //@}
+
/**
* @return GenericDAO
**/
public function dropIdentityMap()
{
$this->identityMap = array();
-
+
return $this;
}
@@ -338,34 +367,46 @@ public function dropObjectIdentityMapById($id)
return $this;
}
-
+
+ public function disableTriggers() {
+ $this->triggersAllowed = false;
+ return $this;
+ }
+
+ public function enableTriggers() {
+ $this->triggersAllowed = true;
+ return $this;
+ }
+
protected function inject(
InsertOrUpdateQuery $query,
Identifiable $object
)
{
$this->checkObjectType($object);
-
- return $this->doInject(
+
+ $this->runTrigger($object, 'onBeforeSave');
+
+ return $this->doInject(
$this->setQueryFields(
$query->setTable($this->getTable()), $object
),
$object
);
}
-
+
protected function doInject(
InsertOrUpdateQuery $query,
Identifiable $object
)
{
$db = DBPool::getByDao($this);
-
+
if (!$db->isQueueActive()) {
$count = $db->queryCount($query);
-
+
$this->uncacheById($object->getId());
-
+
if ($count !== 1)
throw new WrongStateException(
$count.' rows affected: racy or insane inject happened: '
@@ -373,14 +414,18 @@ protected function doInject(
);
} else {
$db->queryNull($query);
-
+
$this->uncacheById($object->getId());
}
-
+
// clean out Identifier, if any
- return $this->addObjectToMap($object->setId($object->getId()));
+ $result = $this->addObjectToMap($object->setId($object->getId()));
+
+ $this->runTrigger($object, 'onAfterSave');
+
+ return $result;
}
-
+
/* void */ protected function checkObjectType(Identifiable $object)
{
Assert::isSame(
@@ -389,18 +434,52 @@ protected function doInject(
'strange object given, i can not inject it'
);
}
-
+
private function addObjectToMap(Identifiable $object)
{
return $this->identityMap[$object->getId()] = $object;
}
-
+
private function addObjectListToMap($list)
{
foreach ($list as $object)
$this->identityMap[$object->getId()] = $object;
-
+
return $list;
}
+
+ protected final function runTrigger($input, $triggerName) {
+ call_user_func($this->prepareTrigger($input, $triggerName));
+
+ return $this;
+ }
+
+ protected final function prepareTrigger($input, $triggerName) {
+ if(
+ !$this->triggersAllowed ||
+ !in_array($triggerName, class_implements($objName = $this->getObjectName()))
+ ) {
+ return (function(){ });
+ }
+
+ $check = function($obj) use ($objName) {
+ if(!($obj instanceof $objName)) {
+ $obj = $this->getById($obj);
+ }
+ return $obj;
+ };
+
+ if(is_array($input)) {
+ $input = array_map($check, $input);
+ } else {
+ $input = array($check($input));
+ }
+
+ return function() use (&$input, $triggerName) {
+ foreach($input as $obj) {
+ call_user_func(array($obj, $triggerName));
+ }
+ };
+ }
}
?>
\ No newline at end of file
diff --git a/main/DAOs/Handlers/ApcSegmentHandler.class.php b/main/DAOs/Handlers/ApcSegmentHandler.class.php
old mode 100644
new mode 100755
diff --git a/main/DAOs/Handlers/CacheSegmentHandler.class.php b/main/DAOs/Handlers/CacheSegmentHandler.class.php
old mode 100644
new mode 100755
diff --git a/main/DAOs/Handlers/FileSystemSegmentHandler.class.php b/main/DAOs/Handlers/FileSystemSegmentHandler.class.php
old mode 100644
new mode 100755
diff --git a/main/DAOs/Handlers/MessageSegmentHandler.class.php b/main/DAOs/Handlers/MessageSegmentHandler.class.php
old mode 100644
new mode 100755
diff --git a/main/DAOs/Handlers/OptimizerSegmentHandler.class.php b/main/DAOs/Handlers/OptimizerSegmentHandler.class.php
old mode 100644
new mode 100755
diff --git a/main/DAOs/Handlers/SegmentHandler.class.php b/main/DAOs/Handlers/SegmentHandler.class.php
old mode 100644
new mode 100755
diff --git a/main/DAOs/Handlers/SharedMemorySegmentHandler.class.php b/main/DAOs/Handlers/SharedMemorySegmentHandler.class.php
old mode 100644
new mode 100755
diff --git a/main/DAOs/Handlers/XCacheSegmentHandler.class.php b/main/DAOs/Handlers/XCacheSegmentHandler.class.php
old mode 100644
new mode 100755
diff --git a/main/DAOs/Handlers/eAcceleratorSegmentHandler.class.php b/main/DAOs/Handlers/eAcceleratorSegmentHandler.class.php
old mode 100644
new mode 100755
diff --git a/main/DAOs/Interfaces/onAfterDrop.class.php b/main/DAOs/Interfaces/onAfterDrop.class.php
new file mode 100644
index 0000000000..e11c2029d8
--- /dev/null
+++ b/main/DAOs/Interfaces/onAfterDrop.class.php
@@ -0,0 +1,10 @@
+
+ * @date 2014.05.19
+ */
+
+interface onAfterDrop {
+ public function onAfterDrop();
+}
\ No newline at end of file
diff --git a/main/DAOs/Interfaces/onAfterSave.class.php b/main/DAOs/Interfaces/onAfterSave.class.php
new file mode 100644
index 0000000000..e4cc8a4b19
--- /dev/null
+++ b/main/DAOs/Interfaces/onAfterSave.class.php
@@ -0,0 +1,10 @@
+
+ * @date 2014.05.19
+ */
+
+interface onAfterSave {
+ public function onAfterSave();
+}
\ No newline at end of file
diff --git a/main/DAOs/Interfaces/onBeforeDrop.class.php b/main/DAOs/Interfaces/onBeforeDrop.class.php
new file mode 100644
index 0000000000..d8b9c8b94d
--- /dev/null
+++ b/main/DAOs/Interfaces/onBeforeDrop.class.php
@@ -0,0 +1,10 @@
+
+ * @date 2014.05.19
+ */
+
+interface onBeforeDrop {
+ public function onBeforeDrop();
+}
\ No newline at end of file
diff --git a/main/DAOs/Interfaces/onBeforeSave.class.php b/main/DAOs/Interfaces/onBeforeSave.class.php
new file mode 100644
index 0000000000..394e87c397
--- /dev/null
+++ b/main/DAOs/Interfaces/onBeforeSave.class.php
@@ -0,0 +1,10 @@
+
+ * @date 2014.05.19
+ */
+
+interface onBeforeSave {
+ public function onBeforeSave();
+}
\ No newline at end of file
diff --git a/main/DAOs/ProtoDAO.class.php b/main/DAOs/ProtoDAO.class.php
old mode 100644
new mode 100755
index f2f12d7c38..07f2893374
--- a/main/DAOs/ProtoDAO.class.php
+++ b/main/DAOs/ProtoDAO.class.php
@@ -12,7 +12,7 @@
/**
* @ingroup DAOs
**/
- abstract class ProtoDAO extends GenericDAO
+ abstract class ProtoDAO extends TranslatableDAO
{
public function getJoinPrefix($field, $prefix = null)
{
@@ -165,7 +165,7 @@ protected function setQueryFields(InsertOrUpdateQuery $query, $object)
return $this->getProtoClass()->fillQuery($query, $object);
}
- private function processPath(
+ final protected function processPath(
AbstractProtoClass $proto,
$probablyPath,
JoinCapableQuery $query,
@@ -376,7 +376,15 @@ public function guessAtom(
$mapping = $this->getMapping()
)
) {
- return new DBField($mapping[$atom], $table);
+ if ($this->isTranslatedField($atom)) {
+ return DBHstoreField::create(
+ $mapping[$atom],
+ $table,
+ $this->getLanguageCode()
+ );
+ } else {
+ return new DBField($mapping[$atom], $table);
+ }
} elseif (
($query instanceof SelectQuery)
&& $query->hasAliasInside($atom)
diff --git a/main/DAOs/StorableDAO.class.php b/main/DAOs/StorableDAO.class.php
old mode 100644
new mode 100755
index 4787790be7..cbd031db10
--- a/main/DAOs/StorableDAO.class.php
+++ b/main/DAOs/StorableDAO.class.php
@@ -24,10 +24,16 @@ public function take(Identifiable $object)
public function add(Identifiable $object)
{
+ //Support non-"id" identifier columns
+ if (method_exists($this, 'getIdName')) {
+ $method = 'set'.ucfirst($this->getIdName());
+ } else {
+ $method = 'setId';
+ }
return
$this->inject(
OSQL::insert(),
- $object->setId(
+ $object->{$method}(
DBPool::getByDao($this)->obtainSequence(
$this->getSequence()
)
@@ -81,8 +87,10 @@ public function unite(
$object->getId(), $old->getId(),
'cannot merge different objects'
);
-
- $query = OSQL::update($this->getTable());
+
+ $this->runTrigger($object, 'onBeforeSave');
+
+ $query = OSQL::update($this->getTable());
foreach ($this->getProtoClass()->getPropertyList() as $property) {
$getter = $property->getGetter();
diff --git a/main/DAOs/TranslatableDAO.class.php b/main/DAOs/TranslatableDAO.class.php
new file mode 100644
index 0000000000..8e27f5a099
--- /dev/null
+++ b/main/DAOs/TranslatableDAO.class.php
@@ -0,0 +1,29 @@
+getProtoClass();
+ return $proto->isPropertyExists($name)
+ && $proto->getPropertyByName($name)->isTranslated();
+ }
+
+ public function getLanguageCode() {
+ return call_user_func(array($this->getObjectName(), 'getLanguageCode'));
+ }
+}
\ No newline at end of file
diff --git a/main/DAOs/Workers/BaseDaoWorker.class.php b/main/DAOs/Workers/BaseDaoWorker.class.php
old mode 100644
new mode 100755
index fd67f3d22f..1a2e88fdbb
--- a/main/DAOs/Workers/BaseDaoWorker.class.php
+++ b/main/DAOs/Workers/BaseDaoWorker.class.php
@@ -93,8 +93,14 @@ public function uncacheByQuery(SelectQuery $query)
{
return
Cache::me()->mark($this->className)->
- delete($this->makeQueryKey($query, self::SUFFIX_QUERY));
+ delete($this->makeQueryKey($query, $this->getSuffixQuery()));
}
+
+ public function uncacheItems() {}
+
+ protected function getSuffixQuery() {
+ return self::SUFFIX_QUERY;
+ }
//@}
/// cache getters
@@ -110,7 +116,7 @@ protected function getCachedByQuery(SelectQuery $query)
{
return
Cache::me()->mark($this->className)->
- get($this->makeQueryKey($query, self::SUFFIX_QUERY));
+ get($this->makeQueryKey($query, $this->getSuffixQuery()));
}
//@}
diff --git a/main/DAOs/Workers/CacheDaoWorker.class.php b/main/DAOs/Workers/CacheDaoWorker.class.php
old mode 100644
new mode 100755
diff --git a/main/DAOs/Workers/CommonDaoWorker.class.php b/main/DAOs/Workers/CommonDaoWorker.class.php
old mode 100644
new mode 100755
index 0da3497dbf..6f72db4404
--- a/main/DAOs/Workers/CommonDaoWorker.class.php
+++ b/main/DAOs/Workers/CommonDaoWorker.class.php
@@ -199,43 +199,37 @@ public function getListByIds(
$toFetch += array_keys($prefixed);
if ($toFetch) {
- try {
- $list =
- array_merge(
- $list,
- $this->getListByLogic(
- Expression::in(
- new DBField(
- $this->dao->getIdName(),
- $this->dao->getTable()
- ),
- $toFetch
- ),
- Cache::DO_NOT_CACHE
- )
- );
- } catch (ObjectNotFoundException $e) {
- // nothing to fetch
- }
- }
- } elseif (count($ids)) {
- try {
$list =
- $this->getListByLogic(
- Expression::in(
- new DBField(
- $this->dao->getIdName(),
- $this->dao->getTable()
- ),
- $ids
- ),
- Cache::DO_NOT_CACHE
+ array_merge(
+ $list,
+ $this->fetchListByIds($toFetch, $expires)
);
- } catch (ObjectNotFoundException $e) {/*_*/}
+ }
+
+ } elseif (count($ids)) {
+ $list = $this->fetchListByIds($ids, $expires);
}
return $list;
}
+
+ protected function fetchListByIds(array $ids, $expires = Cache::DO_NOT_CACHE) {
+ try {
+ return $this->getListByLogic(
+ Expression::in(
+ new DBField(
+ $this->dao->getIdName(),
+ $this->dao->getTable()
+ ),
+ $ids
+ ),
+ $expires
+ );
+
+ } catch (ObjectNotFoundException $e) {
+ return array();
+ }
+ }
public function getListByQuery(
SelectQuery $query, $expires = Cache::DO_NOT_CACHE
@@ -447,7 +441,7 @@ protected function cacheByQuery(
Cache::me()->mark($this->className)->
add(
- $this->makeQueryKey($query, self::SUFFIX_QUERY),
+ $this->makeQueryKey($query, $this->getSuffixQuery()),
$object,
$expires
);
@@ -471,8 +465,9 @@ public function dropById($id)
{
$result = parent::dropById($id);
- $this->dao->uncacheLists();
-
+// $this->dao->uncacheLists();
+ $this->uncacheLists();
+
return $result;
}
//@}
@@ -481,7 +476,8 @@ public function dropById($id)
//@{
public function uncacheById($id)
{
- $this->dao->uncacheLists();
+// $this->dao->uncacheLists();
+ $this->uncacheLists();
return parent::uncacheById($id);
}
diff --git a/main/DAOs/Workers/CustomDataScopedWorker.class.php b/main/DAOs/Workers/CustomDataScopedWorker.class.php
old mode 100644
new mode 100755
diff --git a/main/DAOs/Workers/DalayedDropDaoWorker.class.php b/main/DAOs/Workers/DalayedDropDaoWorker.class.php
old mode 100644
new mode 100755
diff --git a/main/DAOs/Workers/LinkedDaoWorker.class.php b/main/DAOs/Workers/LinkedDaoWorker.class.php
new file mode 100644
index 0000000000..572a55d8ec
--- /dev/null
+++ b/main/DAOs/Workers/LinkedDaoWorker.class.php
@@ -0,0 +1,228 @@
+getByQuery($this->makeIdKey($id, false), $expires);
+ }
+
+ protected function makeIdKey($id, $toString = true) {
+ /** @var SelectQuery $query */
+ $query =
+ $this->dao->
+ makeSelectHead()->
+ andWhere(
+ Expression::eq(
+ DBField::create(
+ $this->dao->getIdName(),
+ $this->dao->getTable()
+ ),
+ $id
+ )
+ );
+ if ($toString) {
+ return $this->makeQueryKey($query, self::SUFFIX_ITEM);
+ } else {
+ return $query;
+ }
+ }
+
+ /**
+ * @return $this
+ */
+ private function setSuffixItem() {
+ $this->suffix = self::SUFFIX_ITEM;
+ return $this;
+ }
+
+ /**
+ * @return $this
+ */
+ private function setSuffixList() {
+ $this->suffix = self::SUFFIX_LIST;
+ return $this;
+ }
+
+ /**
+ * @return $this
+ */
+ private function setSuffixCustom() {
+ $this->suffix = self::SUFFIX_CUSTOM;
+ return $this;
+ }
+
+ protected function getSuffixQuery() {
+ return $this->suffix;
+ }
+
+ public function getByQuery(SelectQuery $query, $expires = Cache::DO_NOT_CACHE) {
+ $this->setSuffixItem();
+ return parent::getByQuery($query, $expires);
+ }
+
+ public function getCustom(SelectQuery $query, $expires = Cache::DO_NOT_CACHE) {
+ $this->setSuffixCustom();
+ return parent::getCustom($query, $expires);
+ }
+
+ public function getListByQuery(SelectQuery $query, $expires = Cache::DO_NOT_CACHE) {
+ $this->setSuffixList();
+ return parent::getListByQuery($query, $expires);
+ }
+
+ public function getCustomList(SelectQuery $query, $expires = Cache::DO_NOT_CACHE) {
+ $this->setSuffixCustom();
+ return parent::getCustomList($query, $expires);
+ }
+
+ public function getCustomRowList(SelectQuery $query, $expires = Cache::DO_NOT_CACHE) {
+ $this->setSuffixCustom();
+ return parent::getCustomRowList($query, $expires);
+ }
+
+ public function getQueryResult(SelectQuery $query, $expires = Cache::DO_NOT_CACHE) {
+ $this->setSuffixCustom();
+ return parent::getQueryResult($query, $expires);
+ }
+
+ public function uncacheByIds($ids) {
+ $keys = array();
+ foreach ($ids as $id) {
+ $keys[] = $this->makeIdKey($id);
+ }
+
+ $isUncacheIds = Cache::me()
+ ->mark($this->className)
+ ->deleteList($keys)
+ ;
+
+ return $isUncacheIds && $this->dao->uncacheLists();
+ }
+
+ public function uncacheLists() {
+ return Cache::me()
+ ->mark($this->className)
+ ->deleteByPattern($this->className . self::SUFFIX_LIST)
+ ;
+ }
+
+ public function uncacheItems() {
+ return Cache::me()
+ ->mark($this->className)
+ ->deleteByPattern($this->className . self::SUFFIX_ITEM)
+ ;
+ }
+
+ protected function cacheByQuery(
+ SelectQuery $query,
+ /* Identifiable */ $object,
+ $expires = Cache::DO_NOT_CACHE
+ )
+ {
+ if ($expires !== Cache::DO_NOT_CACHE) {
+
+ if (self::SUFFIX_ITEM == $this->getSuffixQuery()) {
+
+ $idKey = $this->makeIdKey($object->getId());
+ $queryKey = $this->makeQueryKey($query, $this->getSuffixQuery());
+
+ if ($idKey != $queryKey) {
+ Cache::me()
+ ->mark($this->className)
+ ->add(
+ $queryKey,
+ CacheLink::create()
+ ->setKey($idKey),
+ $expires
+ );
+ }
+
+ Cache::me()
+ ->mark($this->className)
+ ->add(
+ $idKey,
+ $object,
+ $expires
+ );
+ } else if (self::SUFFIX_LIST == $this->getSuffixQuery()) {
+ /** @var CacheListLink $link */
+ $link = CacheListLink::create();
+ foreach ($object as $item) {
+ $idKey = $this->makeIdKey($item->getId());
+
+ Cache::me()
+ ->mark($this->className)
+ ->add(
+ $idKey,
+ $item,
+ $expires
+ );
+
+ $link->setKey($item->getId(), $idKey);
+ }
+
+ parent::cacheByQuery($query, $link, $expires);
+ } else {
+ parent::cacheByQuery($query, $object, $expires);
+ }
+ }
+
+ return $object;
+ }
+
+ protected function getCachedByQuery(SelectQuery $query)
+ {
+ $object = Cache::me()
+ ->mark($this->className)
+ ->get(
+ $this->makeQueryKey(
+ $query,
+ $this->getSuffixQuery()
+ )
+ )
+ ;
+
+ if ($object instanceof CacheLink) {
+ $object = Cache::me()->get($object->getKey());
+ } else if ($object instanceof CacheListLink) {
+
+ $keys = $object->getKeys();
+ $object = Cache::me()->getList($keys);
+
+ foreach ($keys as $id => $key) {
+ if (!$object[$key]) {
+ try {
+ $item = $this->dao->getById($id);
+ $object[$key] = $item;
+ Cache::me()
+ ->mark($this->className)
+ ->add(
+ $this->makeIdKey($id),
+ $item,
+ Cache::EXPIRES_MEDIUM
+ );
+ } catch (ObjectNotFoundException $e) {
+ unset($object[$key]);
+ }
+ }
+ }
+
+ $object = array_values($object);
+ }
+
+ return $object;
+ }
+}
\ No newline at end of file
diff --git a/main/DAOs/Workers/NullDaoWorker.class.php b/main/DAOs/Workers/NullDaoWorker.class.php
old mode 100644
new mode 100755
diff --git a/main/DAOs/Workers/SmartDaoWorker.class.php b/main/DAOs/Workers/SmartDaoWorker.class.php
old mode 100644
new mode 100755
diff --git a/main/DAOs/Workers/TransparentDaoWorker.class.php b/main/DAOs/Workers/TransparentDaoWorker.class.php
old mode 100644
new mode 100755
diff --git a/main/DAOs/Workers/TrickyDaoWorker.class.php b/main/DAOs/Workers/TrickyDaoWorker.class.php
new file mode 100755
index 0000000000..b1187368c9
--- /dev/null
+++ b/main/DAOs/Workers/TrickyDaoWorker.class.php
@@ -0,0 +1,37 @@
+isNoSqlDao() ) {
+ return true;
+ }
+
+ return parent::uncacheLists();
+ }
+
+ /**
+ * Проверяем является ли текущий DAO реализацией NoSqlDAO
+ * @return bool
+ */
+ protected function isNoSqlDao() {
+ return ($this->dao instanceof NoSqlDAO);
+ }
+
+
+ }
diff --git a/main/DAOs/Workers/VoodooDaoWorker.class.php b/main/DAOs/Workers/VoodooDaoWorker.class.php
old mode 100644
new mode 100755
diff --git a/main/EntityProto/Accessors/DTOGetter.class.php b/main/EntityProto/Accessors/DTOGetter.class.php
old mode 100644
new mode 100755
diff --git a/main/EntityProto/Accessors/DTOSetter.class.php b/main/EntityProto/Accessors/DTOSetter.class.php
old mode 100644
new mode 100755
diff --git a/main/EntityProto/Accessors/DirectoryGetter.class.php b/main/EntityProto/Accessors/DirectoryGetter.class.php
old mode 100644
new mode 100755
diff --git a/main/EntityProto/Accessors/DirectoryMutator.class.php b/main/EntityProto/Accessors/DirectoryMutator.class.php
old mode 100644
new mode 100755
diff --git a/main/EntityProto/Accessors/DirectorySetter.class.php b/main/EntityProto/Accessors/DirectorySetter.class.php
old mode 100644
new mode 100755
diff --git a/main/EntityProto/Accessors/FormExporter.class.php b/main/EntityProto/Accessors/FormExporter.class.php
old mode 100644
new mode 100755
diff --git a/main/EntityProto/Accessors/FormGetter.class.php b/main/EntityProto/Accessors/FormGetter.class.php
old mode 100644
new mode 100755
diff --git a/main/EntityProto/Accessors/FormHardenedSetter.class.php b/main/EntityProto/Accessors/FormHardenedSetter.class.php
old mode 100644
new mode 100755
diff --git a/main/EntityProto/Accessors/FormImporter.class.php b/main/EntityProto/Accessors/FormImporter.class.php
old mode 100644
new mode 100755
diff --git a/main/EntityProto/Accessors/FormMutator.class.php b/main/EntityProto/Accessors/FormMutator.class.php
old mode 100644
new mode 100755
diff --git a/main/EntityProto/Accessors/FormSetter.class.php b/main/EntityProto/Accessors/FormSetter.class.php
old mode 100644
new mode 100755
diff --git a/main/EntityProto/Accessors/ObjectGetter.class.php b/main/EntityProto/Accessors/ObjectGetter.class.php
old mode 100644
new mode 100755
diff --git a/main/EntityProto/Accessors/ObjectSetter.class.php b/main/EntityProto/Accessors/ObjectSetter.class.php
old mode 100644
new mode 100755
diff --git a/main/EntityProto/Accessors/ScopeGetter.class.php b/main/EntityProto/Accessors/ScopeGetter.class.php
old mode 100644
new mode 100755
diff --git a/main/EntityProto/Accessors/ScopeSetter.class.php b/main/EntityProto/Accessors/ScopeSetter.class.php
old mode 100644
new mode 100755
diff --git a/main/EntityProto/Builders/DTOBuilder.class.php b/main/EntityProto/Builders/DTOBuilder.class.php
old mode 100644
new mode 100755
diff --git a/main/EntityProto/Builders/DTOToFormImporter.class.php b/main/EntityProto/Builders/DTOToFormImporter.class.php
old mode 100644
new mode 100755
diff --git a/main/EntityProto/Builders/DTOToScopeConverter.class.php b/main/EntityProto/Builders/DTOToScopeConverter.class.php
old mode 100644
new mode 100755
diff --git a/main/EntityProto/Builders/DirectoryBuilder.class.php b/main/EntityProto/Builders/DirectoryBuilder.class.php
old mode 100644
new mode 100755
diff --git a/main/EntityProto/Builders/DirectoryToObjectBinder.class.php b/main/EntityProto/Builders/DirectoryToObjectBinder.class.php
old mode 100644
new mode 100755
diff --git a/main/EntityProto/Builders/FormBuilder.class.php b/main/EntityProto/Builders/FormBuilder.class.php
old mode 100644
new mode 100755
diff --git a/main/EntityProto/Builders/FormToObjectConverter.class.php b/main/EntityProto/Builders/FormToObjectConverter.class.php
old mode 100644
new mode 100755
diff --git a/main/EntityProto/Builders/FormToScopeExporter.class.php b/main/EntityProto/Builders/FormToScopeExporter.class.php
old mode 100644
new mode 100755
diff --git a/main/EntityProto/Builders/ObjectBuilder.class.php b/main/EntityProto/Builders/ObjectBuilder.class.php
old mode 100644
new mode 100755
diff --git a/main/EntityProto/Builders/ObjectToDTOConverter.class.php b/main/EntityProto/Builders/ObjectToDTOConverter.class.php
old mode 100644
new mode 100755
diff --git a/main/EntityProto/Builders/ObjectToDirectoryBinder.class.php b/main/EntityProto/Builders/ObjectToDirectoryBinder.class.php
old mode 100644
new mode 100755
diff --git a/main/EntityProto/Builders/ObjectToFormConverter.class.php b/main/EntityProto/Builders/ObjectToFormConverter.class.php
old mode 100644
new mode 100755
diff --git a/main/EntityProto/Builders/ObjectToFormSetter.class.php b/main/EntityProto/Builders/ObjectToFormSetter.class.php
old mode 100644
new mode 100755
diff --git a/main/EntityProto/Builders/ObjectToObjectCast.class.php b/main/EntityProto/Builders/ObjectToObjectCast.class.php
old mode 100644
new mode 100755
diff --git a/main/EntityProto/Builders/ScopeToFormImporter.class.php b/main/EntityProto/Builders/ScopeToFormImporter.class.php
old mode 100644
new mode 100755
diff --git a/main/EntityProto/DirectoryContext.class.php b/main/EntityProto/DirectoryContext.class.php
old mode 100644
new mode 100755
diff --git a/main/EntityProto/EntityProto.class.php b/main/EntityProto/EntityProto.class.php
old mode 100644
new mode 100755
diff --git a/main/EntityProto/PrototypedBuilder.class.php b/main/EntityProto/PrototypedBuilder.class.php
old mode 100644
new mode 100755
diff --git a/main/EntityProto/PrototypedEntity.class.php b/main/EntityProto/PrototypedEntity.class.php
old mode 100644
new mode 100755
diff --git a/main/EntityProto/PrototypedGetter.class.php b/main/EntityProto/PrototypedGetter.class.php
old mode 100644
new mode 100755
diff --git a/main/EntityProto/PrototypedMethodCaller.class.php b/main/EntityProto/PrototypedMethodCaller.class.php
old mode 100644
new mode 100755
diff --git a/main/EntityProto/PrototypedSetter.class.php b/main/EntityProto/PrototypedSetter.class.php
old mode 100644
new mode 100755
diff --git a/main/Flow/AddCommand.class.php b/main/Flow/AddCommand.class.php
old mode 100644
new mode 100755
diff --git a/main/Flow/BaseEditor.class.php b/main/Flow/BaseEditor.class.php
old mode 100644
new mode 100755
diff --git a/main/Flow/CarefulCommand.class.php b/main/Flow/CarefulCommand.class.php
old mode 100644
new mode 100755
diff --git a/main/Flow/CarefulDatabaseRunner.class.php b/main/Flow/CarefulDatabaseRunner.class.php
old mode 100644
new mode 100755
diff --git a/main/Flow/CommandChain.class.php b/main/Flow/CommandChain.class.php
old mode 100644
new mode 100755
diff --git a/main/Flow/Controller.class.php b/main/Flow/Controller.class.php
old mode 100644
new mode 100755
diff --git a/main/Flow/ControllersCollection.class.php b/main/Flow/ControllersCollection.class.php
old mode 100644
new mode 100755
diff --git a/main/Flow/DecoratorController.class.php b/main/Flow/DecoratorController.class.php
old mode 100644
new mode 100755
diff --git a/main/Flow/DropCommand.class.php b/main/Flow/DropCommand.class.php
old mode 100644
new mode 100755
diff --git a/main/Flow/EditCommand.class.php b/main/Flow/EditCommand.class.php
old mode 100644
new mode 100755
diff --git a/main/Flow/EditorCommand.class.php b/main/Flow/EditorCommand.class.php
old mode 100644
new mode 100755
diff --git a/main/Flow/EditorController.class.php b/main/Flow/EditorController.class.php
old mode 100644
new mode 100755
diff --git a/main/Flow/ForbiddenCommand.class.php b/main/Flow/ForbiddenCommand.class.php
old mode 100644
new mode 100755
diff --git a/main/Flow/HandlerMapping.class.php b/main/Flow/HandlerMapping.class.php
old mode 100644
new mode 100755
diff --git a/main/Flow/HttpRequest.class.php b/main/Flow/HttpRequest.class.php
old mode 100644
new mode 100755
index 42b1eb25a6..bbb02ab43e
--- a/main/Flow/HttpRequest.class.php
+++ b/main/Flow/HttpRequest.class.php
@@ -82,7 +82,17 @@ public function setGetVar($name, $value)
$this->get[$name] = $value;
return $this;
}
-
+
+ /**
+ * @return HttpRequest
+ **/
+ public function setGetString($get)
+ {
+ $this->get = $get;
+
+ return $this;
+ }
+
public function &getPost()
{
return $this->post;
@@ -116,7 +126,17 @@ public function setPostVar($name, $value)
$this->post[$name] = $value;
return $this;
}
-
+
+ /**
+ * @return HttpRequest
+ **/
+ public function setPostString($post)
+ {
+ $this->post = $post;
+
+ return $this;
+ }
+
public function &getServer()
{
return $this->server;
diff --git a/main/Flow/ImportCommand.class.php b/main/Flow/ImportCommand.class.php
old mode 100644
new mode 100755
diff --git a/main/Flow/MakeCommand.class.php b/main/Flow/MakeCommand.class.php
old mode 100644
new mode 100755
diff --git a/main/Flow/MethodMappedController.class.php b/main/Flow/MethodMappedController.class.php
old mode 100644
new mode 100755
diff --git a/main/Flow/Model.class.php b/main/Flow/Model.class.php
old mode 100644
new mode 100755
diff --git a/main/Flow/ModelAndView.class.php b/main/Flow/ModelAndView.class.php
old mode 100644
new mode 100755
diff --git a/main/Flow/MonolithicController.class.php b/main/Flow/MonolithicController.class.php
old mode 100644
new mode 100755
diff --git a/main/Flow/NullController.class.php b/main/Flow/NullController.class.php
old mode 100644
new mode 100755
diff --git a/main/Flow/PrototypedEditor.class.php b/main/Flow/PrototypedEditor.class.php
old mode 100644
new mode 100755
diff --git a/main/Flow/ProxyController.class.php b/main/Flow/ProxyController.class.php
old mode 100644
new mode 100755
diff --git a/main/Flow/SaveCommand.class.php b/main/Flow/SaveCommand.class.php
old mode 100644
new mode 100755
diff --git a/main/Flow/TakeCommand.class.php b/main/Flow/TakeCommand.class.php
old mode 100644
new mode 100755
diff --git a/main/Markup/Csv.class.php b/main/Markup/Csv.class.php
old mode 100644
new mode 100755
diff --git a/main/Markup/Feed/AtomChannelWorker.class.php b/main/Markup/Feed/AtomChannelWorker.class.php
old mode 100644
new mode 100755
diff --git a/main/Markup/Feed/AtomFeedFormat.class.php b/main/Markup/Feed/AtomFeedFormat.class.php
old mode 100644
new mode 100755
diff --git a/main/Markup/Feed/AtomItemWorker.class.php b/main/Markup/Feed/AtomItemWorker.class.php
old mode 100644
new mode 100755
diff --git a/main/Markup/Feed/FeedChannel.class.php b/main/Markup/Feed/FeedChannel.class.php
old mode 100644
new mode 100755
diff --git a/main/Markup/Feed/FeedChannelWorker.class.php b/main/Markup/Feed/FeedChannelWorker.class.php
old mode 100644
new mode 100755
diff --git a/main/Markup/Feed/FeedFormat.class.php b/main/Markup/Feed/FeedFormat.class.php
old mode 100644
new mode 100755
diff --git a/main/Markup/Feed/FeedItem.class.php b/main/Markup/Feed/FeedItem.class.php
old mode 100644
new mode 100755
diff --git a/main/Markup/Feed/FeedItemContent.class.php b/main/Markup/Feed/FeedItemContent.class.php
old mode 100644
new mode 100755
diff --git a/main/Markup/Feed/FeedItemContentType.class.php b/main/Markup/Feed/FeedItemContentType.class.php
old mode 100644
new mode 100755
diff --git a/main/Markup/Feed/FeedItemWorker.class.php b/main/Markup/Feed/FeedItemWorker.class.php
old mode 100644
new mode 100755
diff --git a/main/Markup/Feed/FeedReader.class.php b/main/Markup/Feed/FeedReader.class.php
old mode 100644
new mode 100755
diff --git a/main/Markup/Feed/RssChannelWorker.class.php b/main/Markup/Feed/RssChannelWorker.class.php
old mode 100644
new mode 100755
diff --git a/main/Markup/Feed/RssFeedFormat.class.php b/main/Markup/Feed/RssFeedFormat.class.php
old mode 100644
new mode 100755
diff --git a/main/Markup/Feed/RssItemWorker.class.php b/main/Markup/Feed/RssItemWorker.class.php
old mode 100644
new mode 100755
diff --git a/main/Markup/Feed/YandexRssFeedFormat.class.php b/main/Markup/Feed/YandexRssFeedFormat.class.php
old mode 100644
new mode 100755
diff --git a/main/Markup/Feed/YandexRssFeedItem.class.php b/main/Markup/Feed/YandexRssFeedItem.class.php
old mode 100644
new mode 100755
diff --git a/main/Markup/Feed/YandexRssItemWorker.class.php b/main/Markup/Feed/YandexRssItemWorker.class.php
old mode 100644
new mode 100755
diff --git a/main/Markup/Html/Cdata.class.php b/main/Markup/Html/Cdata.class.php
old mode 100644
new mode 100755
diff --git a/main/Markup/Html/HtmlAssembler.class.php b/main/Markup/Html/HtmlAssembler.class.php
old mode 100644
new mode 100755
diff --git a/main/Markup/Html/HtmlTokenizer.class.php b/main/Markup/Html/HtmlTokenizer.class.php
old mode 100644
new mode 100755
diff --git a/main/Markup/Html/SgmlEndTag.class.php b/main/Markup/Html/SgmlEndTag.class.php
old mode 100644
new mode 100755
diff --git a/main/Markup/Html/SgmlIgnoredTag.class.php b/main/Markup/Html/SgmlIgnoredTag.class.php
old mode 100644
new mode 100755
diff --git a/main/Markup/Html/SgmlOpenTag.class.php b/main/Markup/Html/SgmlOpenTag.class.php
old mode 100644
new mode 100755
diff --git a/main/Markup/Html/SgmlTag.class.php b/main/Markup/Html/SgmlTag.class.php
old mode 100644
new mode 100755
diff --git a/main/Markup/Html/SgmlToken.class.php b/main/Markup/Html/SgmlToken.class.php
old mode 100644
new mode 100755
diff --git a/main/Math/BigInteger.class.php b/main/Math/BigInteger.class.php
old mode 100644
new mode 100755
diff --git a/main/Math/BigNumberFactory.class.php b/main/Math/BigNumberFactory.class.php
old mode 100644
new mode 100755
diff --git a/main/Math/Calculator.class.php b/main/Math/Calculator.class.php
new file mode 100644
index 0000000000..67df124c91
--- /dev/null
+++ b/main/Math/Calculator.class.php
@@ -0,0 +1,204 @@
+
+ * @author Alex Gorbylev
+ * @date 2011.12.23
+ */
+final class Calculator {
+
+ const OP_SUMMARIZE = 1;
+ const OP_MULTIPLY = 2;
+ const OP_PRIORITY = 3;
+
+ // массив значений
+ protected $variables = array();
+
+ // Массив операций в строке
+ protected $formula;
+
+ // Номер текущей операции в строке
+ protected $index;
+
+ // Стек значений в зависимости от последовательности операций
+ protected $stack;
+
+ // результат по умолчанию
+ protected $default = 0;
+
+ protected $operators = array(
+ '+' => self::OP_SUMMARIZE,
+ '-' => self::OP_SUMMARIZE,
+
+ '*' => self::OP_MULTIPLY,
+ '/' => self::OP_MULTIPLY,
+ '%' => self::OP_MULTIPLY,
+
+ '(' => self::OP_PRIORITY,
+ );
+
+ final protected function __construct() {/*_*/}
+
+ /**
+ * @param string $formula
+ * @param array $input
+ * @return int
+ */
+ public static function execute($formula, array $input = null) {
+ $calc = new self();
+ $input = is_array($input) && !empty($input) ? $input : array();
+ // запускаем!
+ return $calc->calculate($formula, $input );
+ }
+
+ /**
+ * Расчет конечного результата в зависимости от условия
+ */
+ public function calculate( $math, array $input ) {
+ $this->formula = array();
+ $this->stack = array();
+
+ $math = strtolower( $math );
+ // проверяем на только допустимые символы
+ if ( preg_replace( '/[0-9\(\)\s\+\-\*\/\%\$\.]+/', '', $math ) !== '' ) {
+ throw new WrongArgumentException('Formula contains unsupported symbols');
+ }
+ // делаем по пробелу между элементами
+ $math = preg_replace('/\s+/', '', $math);
+ $math = preg_replace( '/([\(\)\+\-\*\/\%]{1})/', ' $1 ', $math );
+ $math = trim( preg_replace('/\s+/', ' ', $math) );
+ // разбиваем формулу на составные части
+ $this->formula = explode( ' ', $math );
+
+ // Инициализация
+ $this->variables = $input;
+ $this->index = 0;
+
+ $this->calculateSummarize();
+ return
+ isset( $this->stack[0] )
+ ? $this->stack[0]
+ : $this->default
+ ;
+ }
+
+ /**
+ * Рассчитывает операцию
+ */
+ protected function evaluateVariables( $operation ) {
+ $p2 = array_pop ( $this->stack );
+ $p1 = array_pop ( $this->stack );
+
+ $res = $this->default;
+ switch($operation) {
+ case '+': {
+ $res = $p1 + $p2;
+ } break;
+ case '-': {
+ $res = $p1 - $p2;
+ } break;
+ case '*': {
+ $res = $p1 * $p2;
+ } break;
+ case '/': {
+ $res = $p1 / $p2;
+ } break;
+ case '%': {
+ $res = $p1 % $p2;
+ } break;
+ default: {
+ throw new WrongArgumentException("Unknown math operation '{$operation}'");
+ } break;
+ }
+
+ array_push( $this->stack, $res );
+ }
+
+ /**
+ * Расчет строки как формулы
+ */
+ protected function calculateSummarize() {
+ $this->calculateMultiply();
+
+ if($this->checkStop()){ return; }
+
+ if ( $this->getCurrentOperatorClass() == self::OP_SUMMARIZE ) {
+ $operator = $this->getCurrent();
+ $this->next();
+ $this->calculateSummarize();
+ $this->evaluateVariables( $operator );
+ return;
+ }
+ }
+
+ /**
+ * Расчет множителей
+ */
+ protected function calculateMultiply() {
+ $this->calculatePriority();
+
+ if($this->checkStop()){ return; }
+
+ if ( $this->getCurrentOperatorClass() == self::OP_MULTIPLY ) {
+ $operator = $this->getCurrent();
+ $this->next();
+ $this->calculateMultiply();
+ $this->evaluateVariables( $operator );
+ return;
+ }
+ }
+
+ /**
+ * Расчет скобок
+ */
+ protected function calculatePriority() {
+ if ( $this->getCurrentOperatorClass() == self::OP_PRIORITY ) {
+ $this->next();
+ $this->calculateSummarize();
+ $this->next();
+ }
+ else {
+ $this->calculateValue();
+ }
+ }
+
+ /**
+ * Оперирует числом в строке условия
+ */
+ protected function calculateValue() {
+ // значение в строке
+ $operand = $this->getCurrent();
+
+ // получаем значение операнда
+ $value_to_push = $this->default;
+ // типа операнда: константа или переменная
+ if( $operand{0}=='$' ) {
+ $key = intval(substr($operand, 1))-1;
+ $value_to_push = array_key_exists($key, $this->variables) ? $this->variables[$key] : $this->default;
+ } elseif(Assert::checkFloat($operand)) {
+ $value_to_push = floatval($operand);
+ }
+
+ // добавляем элемент в стек для последующей подстановки в выражение
+ array_push( $this->stack, $value_to_push );
+ $this->next();
+ }
+
+ private function next() {
+ $this->index++;
+ }
+
+ private function checkStop() {
+ return $this->index >= count( $this->formula );
+ }
+
+ private function getCurrent() {
+ return $this->formula[$this->index];
+ }
+
+ private function getCurrentOperatorClass() {
+ $exist = isset( $this->operators[ $this->getCurrent() ] );
+ return $exist ? $this->operators[ $this->getCurrent() ] : null;
+ }
+
+}
\ No newline at end of file
diff --git a/main/Math/FileRandomSource.class.php b/main/Math/FileRandomSource.class.php
old mode 100644
new mode 100755
diff --git a/main/Math/GmpBigInteger.class.php b/main/Math/GmpBigInteger.class.php
old mode 100644
new mode 100755
diff --git a/main/Math/GmpBigIntegerFactory.class.php b/main/Math/GmpBigIntegerFactory.class.php
old mode 100644
new mode 100755
diff --git a/main/Math/MathUtils.class.php b/main/Math/MathUtils.class.php
old mode 100644
new mode 100755
diff --git a/main/Math/MtRandomSource.class.php b/main/Math/MtRandomSource.class.php
old mode 100644
new mode 100755
diff --git a/main/Math/RandomSource.class.php b/main/Math/RandomSource.class.php
old mode 100644
new mode 100755
diff --git a/main/Math/TupleFunctor.class.php b/main/Math/TupleFunctor.class.php
old mode 100644
new mode 100755
diff --git a/main/Messages/Interface/Message.class.php b/main/Messages/Interface/Message.class.php
old mode 100644
new mode 100755
diff --git a/main/Messages/Interface/MessageQueue.class.php b/main/Messages/Interface/MessageQueue.class.php
old mode 100644
new mode 100755
diff --git a/main/Messages/Interface/MessageQueueBrowser.class.php b/main/Messages/Interface/MessageQueueBrowser.class.php
old mode 100644
new mode 100755
diff --git a/main/Messages/Interface/MessageQueueReceiver.class.php b/main/Messages/Interface/MessageQueueReceiver.class.php
old mode 100644
new mode 100755
diff --git a/main/Messages/Interface/MessageQueueSender.class.php b/main/Messages/Interface/MessageQueueSender.class.php
old mode 100644
new mode 100755
diff --git a/main/Messages/TextFileQueue.class.php b/main/Messages/TextFileQueue.class.php
old mode 100644
new mode 100755
diff --git a/main/Messages/TextFileQueueBrowser.class.php b/main/Messages/TextFileQueueBrowser.class.php
old mode 100644
new mode 100755
diff --git a/main/Messages/TextFileReceiver.class.php b/main/Messages/TextFileReceiver.class.php
old mode 100644
new mode 100755
diff --git a/main/Messages/TextFileSender.class.php b/main/Messages/TextFileSender.class.php
old mode 100644
new mode 100755
diff --git a/main/Messages/TextMessage.class.php b/main/Messages/TextMessage.class.php
old mode 100644
new mode 100755
diff --git a/main/Monitoring/PinbaClient.class.php b/main/Monitoring/PinbaClient.class.php
old mode 100644
new mode 100755
diff --git a/main/Monitoring/PinbedMemcached.class.php b/main/Monitoring/PinbedMemcached.class.php
old mode 100644
new mode 100755
diff --git a/main/Monitoring/PinbedPeclMemcached.class.php b/main/Monitoring/PinbedPeclMemcached.class.php
old mode 100644
new mode 100755
diff --git a/main/Monitoring/PinbedPgSQL.class.php b/main/Monitoring/PinbedPgSQL.class.php
old mode 100644
new mode 100755
diff --git a/main/Net/GenericUri.class.php b/main/Net/GenericUri.class.php
old mode 100644
new mode 100755
index 3f234ca030..ef531947e3
--- a/main/Net/GenericUri.class.php
+++ b/main/Net/GenericUri.class.php
@@ -285,6 +285,55 @@ public function getQuery()
{
return $this->query;
}
+
+ /**
+ * @return array|null
+ */
+ public function getQueryArray() {
+ if ($this->getQuery()) {
+ parse_str($this->getQuery(), $array);
+ return $array;
+ }
+ return null;
+ }
+
+ /**
+ * @param $array
+ * @return $this
+ */
+ public function setQueryArray($array) {
+ Assert::isArray($array);
+
+ $build = function ($array, $prefix = null) use (&$build) {
+ $pairs = array();
+ foreach ($array as $key => $value) {
+ $key = urlencode($key);
+ if ($prefix) {
+ $key = $prefix . '[' . $key . ']';
+ }
+
+ if (is_object($value)) {
+ throw new WrongArgumentException($key . ' is an object (' . get_class($value) . ')');
+ }
+
+ if (is_array($value)) {
+ foreach ($build($value, $key) as $pair) {
+ $pairs []= $pair;
+ }
+ } else {
+ if (is_string($value)) $value = urlencode($value);
+ if (is_bool($value)) $value = $value ? '1' : '0';
+
+ $pairs []= $key . '=' . $value;
+ }
+ }
+ return $pairs;
+ };
+
+ $this->setQuery( implode('&', $build($array)) );
+
+ return $this;
+ }
/**
* @return GenericUri
diff --git a/main/Net/Http/Cookie.class.php b/main/Net/Http/Cookie.class.php
old mode 100644
new mode 100755
diff --git a/main/Net/Http/CookieCollection.class.php b/main/Net/Http/CookieCollection.class.php
old mode 100644
new mode 100755
diff --git a/main/Net/Http/CurlHttpClient.class.php b/main/Net/Http/CurlHttpClient.class.php
old mode 100644
new mode 100755
index 574371cd9c..be61aa856a
--- a/main/Net/Http/CurlHttpClient.class.php
+++ b/main/Net/Http/CurlHttpClient.class.php
@@ -23,6 +23,8 @@ final class CurlHttpClient implements HttpClient
private $multiRequests = array();
private $multiResponses = array();
private $multiThreadOptions = array();
+
+ private $handler = null;
/**
* @return CurlHttpClient
@@ -61,7 +63,7 @@ public function getOption($key)
}
/**
- * @param $timeout in seconds
+ * @param $timeout int seconds
* @return CurlHttpClient
**/
public function setTimeout($timeout)
@@ -184,12 +186,12 @@ public function getResponse(HttpRequest $request)
/**
* @return HttpResponse
**/
- public function send(HttpRequest $request)
+ public function send(HttpRequest $request, $keepHandler = false)
{
$response = CurlHttpResponse::create()->
setMaxFileSize($this->maxFileSize);
- $handle = $this->makeHandle($request, $response);
+ $handle = $this->makeHandle($request, $response, $keepHandler);
if (curl_exec($handle) === false) {
$code = curl_errno($handle);
@@ -201,8 +203,10 @@ public function send(HttpRequest $request)
}
$this->makeResponse($handle, $response);
-
- curl_close($handle);
+
+ if( !$keepHandler ) {
+ curl_close($handle);
+ }
return $response;
}
@@ -245,15 +249,31 @@ public function multiSend()
return true;
}
+
+ public function close() {
+ if ($this->handler) {
+ curl_close($this->handler);
+ $this->handler = null;
+ }
+ return $this;
+ }
protected function getRequestKey(HttpRequest $request)
{
return md5(serialize($request));
}
- protected function makeHandle(HttpRequest $request, CurlHttpResponse $response)
+ protected function makeHandle(HttpRequest $request, CurlHttpResponse $response, $keepHandler = false)
{
- $handle = curl_init();
+ if( $keepHandler && !is_null($this->handler) ) {
+ $handle = $this->handler;
+ } else {
+ $handle = curl_init();
+ if( $keepHandler ) {
+ $this->handler = $handle;
+ }
+ }
+
Assert::isNotNull($request->getMethod());
$options = array(
@@ -281,15 +301,17 @@ protected function makeHandle(HttpRequest $request, CurlHttpResponse $response)
case HttpMethod::POST:
$options[CURLOPT_POST] = true;
- $options[CURLOPT_POSTFIELDS] =
- $this->argumentsToString($request->getPost());
break;
default:
$options[CURLOPT_CUSTOMREQUEST] = $request->getMethod()->getName();
break;
}
-
+
+ if ($request->getPost()) {
+ $options[CURLOPT_POSTFIELDS] = $this->argumentsToString($request->getPost());
+ }
+
$headers = array();
foreach ($request->getHeaderList() as $headerName => $headerValue) {
$headers[] = "{$headerName}: $headerValue";
@@ -339,21 +361,37 @@ protected function makeResponse($handle, CurlHttpResponse $response)
private function argumentsToString($array)
{
- Assert::isArray($array);
- $result = array();
-
- foreach ($array as $key => $value) {
- if (is_array($value)) {
- foreach ($value as $valueKey => $simpleValue) {
- $result[] =
- $key.'['.$valueKey.']='.urlencode($simpleValue);
+ if( is_array($array) ) {
+ $build = function ($array, $prefix = null) use (&$build) {
+ $pairs = array();
+ foreach ($array as $key => $value) {
+ $key = urlencode($key);
+ if ($prefix) {
+ $key = $prefix . '[' . $key . ']';
+ }
+
+ if (is_object($value)) {
+ throw new WrongArgumentException($key . ' is an object (' . get_class($value) . ')');
+ }
+
+ if (is_array($value)) {
+ foreach ($build($value, $key) as $pair) {
+ $pairs []= $pair;
+ }
+ } else {
+ if (is_string($value)) $value = urlencode($value);
+ if (is_bool($value)) $value = $value ? '1' : '0';
+
+ $pairs []= $key . '=' . $value;
+ }
}
- } else {
- $result[] = $key.'='.urlencode($value);
- }
+ return $pairs;
+ };
+
+ return implode('&', $build($array));
+ } else {
+ return $array;
}
-
- return implode('&', $result);
}
}
?>
\ No newline at end of file
diff --git a/main/Net/Http/CurlHttpResponse.class.php b/main/Net/Http/CurlHttpResponse.class.php
old mode 100644
new mode 100755
diff --git a/main/Net/Http/HeaderParser.class.php b/main/Net/Http/HeaderParser.class.php
old mode 100644
new mode 100755
diff --git a/main/Net/Http/HeaderUtils.class.php b/main/Net/Http/HeaderUtils.class.php
old mode 100644
new mode 100755
diff --git a/main/Net/Http/HttpClient.class.php b/main/Net/Http/HttpClient.class.php
old mode 100644
new mode 100755
diff --git a/main/Net/Http/HttpMethod.class.php b/main/Net/Http/HttpMethod.class.php
old mode 100644
new mode 100755
index 736c720e3f..4fa08f9c06
--- a/main/Net/Http/HttpMethod.class.php
+++ b/main/Net/Http/HttpMethod.class.php
@@ -57,5 +57,15 @@ public static function post()
{
return new self(self::POST);
}
+
+ public static function put()
+ {
+ return new self(self::PUT);
+ }
+
+ public static function delete()
+ {
+ return new self(self::DELETE);
+ }
}
?>
\ No newline at end of file
diff --git a/main/Net/Http/HttpResponse.class.php b/main/Net/Http/HttpResponse.class.php
old mode 100644
new mode 100755
diff --git a/main/Net/Http/HttpStatus.class.php b/main/Net/Http/HttpStatus.class.php
old mode 100644
new mode 100755
diff --git a/main/Net/HttpUrl.class.php b/main/Net/HttpUrl.class.php
old mode 100644
new mode 100755
diff --git a/main/Net/Ip/IpAddress.class.php b/main/Net/Ip/IpAddress.class.php
old mode 100644
new mode 100755
diff --git a/main/Net/Ip/IpCidrRange.class.php b/main/Net/Ip/IpCidrRange.class.php
new file mode 100644
index 0000000000..714fc9f818
--- /dev/null
+++ b/main/Net/Ip/IpCidrRange.class.php
@@ -0,0 +1,112 @@
+
+ * @date 2013.12.02
+ */
+
+/**
+ * @ingroup Ip
+ */
+
+class IpCidrRange implements SingleRange, DialectString, Stringable {
+
+ /** @var IpAddress */
+ protected $startIp;
+
+ /** @var IpAddress */
+ protected $endIp;
+
+ protected $mask;
+
+ /**
+ * @return IpCidrRange
+ **/
+ public static function create($string)
+ {
+ return new static($string);
+ }
+
+ public function __construct( $string ) {
+ Assert::isString($string);
+ if ( preg_match( IpRange::SINGLE_IP_PATTERN, $string ) ) {
+ $ip = IpAddress::create($string);
+ $this->startIp = $ip;
+ $this->endIp = $ip;
+ $this->mask = 32;
+ } elseif ( preg_match( IpRange::IP_SLASH_PATTERN, $string ) ) {
+ list($ip, $mask) = explode('/', $string);
+ $this->createFromSlash($ip, $mask);
+ } elseif ( preg_match( IpRange::IP_WILDCARD_PATTERN, $string ) ) {
+ $ip = substr($string, 0, -2);
+ $mask = substr_count($string, '.') * 8;
+ $this->createFromSlash($ip, $mask);
+
+ } else {
+ throw new WrongArgumentException('strange parameters received');
+ }
+ }
+
+ protected function createFromSlash($ip, $mask)
+ {
+ $ip = IpAddress::createFromCutted($ip);
+
+ if($mask == 32) {
+ $this->startIp = $this->endIp = $ip;
+ $this->mask = $mask;
+ return;
+ }
+
+ if ($mask == 0 || IpRange::MASK_MAX_SIZE < $mask)
+ throw new WrongArgumentException('wrong mask given');
+
+ $longMask =
+ (int) (pow(2, (32 - $mask)) * (pow(2, $mask) - 1));
+
+ if (($ip->getLongIp() & $longMask) != $ip->getLongIp())
+ throw new WrongArgumentException('wrong ip network given');
+
+ $this->startIp = $ip;
+ $this->endIp = IpAddress::create( long2ip($ip->getLongIp() | ~$longMask) );
+ $this->mask = $mask;
+ }
+
+ public function toString() {
+ return $this->getStart()->toString() . '/' . $this->getMask();
+ }
+
+ public function toDialectString(Dialect $dialect) {
+ return $dialect->quoteValue($this->toString());
+ }
+
+ /**
+ * @return IpAddress
+ **/
+ public function getStart()
+ {
+ return $this->startIp;
+ }
+
+ /**
+ * @return IpAddress
+ **/
+ public function getEnd()
+ {
+ return $this->endIp;
+ }
+
+ public function getMask() {
+ return $this->mask;
+ }
+
+ public function contains(/* IpAddress */ $probe) {
+ /** @var IpAddress $probe */
+ Assert::isInstance($probe, 'IpAddress');
+
+ return (
+ ($this->startIp->getLongIp() <= $probe->getLongIp())
+ && ($this->endIp->getLongIp() >= $probe->getLongIp())
+ );
+ }
+
+}
\ No newline at end of file
diff --git a/main/Net/Ip/IpNetwork.class.php b/main/Net/Ip/IpNetwork.class.php
old mode 100644
new mode 100755
diff --git a/main/Net/Ip/IpRange.class.php b/main/Net/Ip/IpRange.class.php
old mode 100644
new mode 100755
index 9d23ef66b1..661be8cee8
--- a/main/Net/Ip/IpRange.class.php
+++ b/main/Net/Ip/IpRange.class.php
@@ -19,7 +19,8 @@ class IpRange implements SingleRange, DialectString, Stringable
const SINGLE_IP_PATTERN = '/^(\d{1,3}\.){3}\d{1,3}$/';
const INTERVAL_PATTERN = '/^\d{1,3}(\.\d{1,3}){3}\s*-\s*\d{1,3}(\.\d{1,3}){3}$/';
const IP_SLASH_PATTERN = '/^(\d{1,3}\.){0,3}\d{1,3}\/\d{1,2}$/';
-
+ const IP_WILDCARD_PATTERN = '/^(\d{1,3}\.){0,3}\*$/';
+
private $startIp = null;
private $endIp = null;
@@ -138,6 +139,11 @@ private function createFromInterval($interval)
private function createFromSlash($ip, $mask)
{
$ip = IpAddress::createFromCutted($ip);
+
+ if($mask == 32) {
+ $this->setup($ip, $ip);
+ return;
+ }
if ($mask == 0 || self::MASK_MAX_SIZE < $mask)
throw new WrongArgumentException('wrong mask given');
@@ -166,10 +172,17 @@ private function createFromString($string)
list($ip, $mask) = explode('/', $string);
$this->createFromSlash($ip, $mask);
- } elseif (preg_match(self::INTERVAL_PATTERN, $string))
- $this->createFromInterval($string);
- else
+ } elseif (preg_match(self::INTERVAL_PATTERN, $string)) {
+ $this->createFromInterval($string);
+
+ } elseif (preg_match(self::IP_WILDCARD_PATTERN, $string)) {
+ $ip = substr($string, 0, -2);
+ $mask = substr_count($string, '.') * 8;
+ $this->createFromSlash($ip, $mask);
+
+ } else
throw new WrongArgumentException('strange parameters received');
}
+
}
?>
\ No newline at end of file
diff --git a/main/Net/Ip/IpUtils.class.php b/main/Net/Ip/IpUtils.class.php
old mode 100644
new mode 100755
index 4a9d5d5cfe..8cfc268229
--- a/main/Net/Ip/IpUtils.class.php
+++ b/main/Net/Ip/IpUtils.class.php
@@ -40,5 +40,21 @@ public static function makeRanges(array $ips)
return $ranges;
}
+
+ public static function checkCIDR($ip, $cidr) {
+ if( !preg_match('@(\d+\.\d+\.\d+\.\d+)(?:/(\d+))?@mi', $cidr, $matches) ) {
+ throw new WrongArgumentException('CIDR is not valid');
+ }
+ $net = $matches[1];
+ $mask = isset($matches[2]) ? $matches[2] : 32;
+ // 2 long
+ $int_ip = ip2long($ip);
+ $int_net = ip2long($net);
+ $int_mask = ~((1 << (32 - $mask)) - 1);
+ // bitwise AND
+ $ip_mask = $int_ip & $int_mask;
+ // check
+ return ($ip_mask == $int_net);
+ }
}
?>
\ No newline at end of file
diff --git a/main/Net/Mail/Mail.class.php b/main/Net/Mail/Mail.class.php
old mode 100644
new mode 100755
index a3530783bd..a26fd1af33
--- a/main/Net/Mail/Mail.class.php
+++ b/main/Net/Mail/Mail.class.php
@@ -27,8 +27,9 @@ final class Mail
private $contentType = null;
private $returnPath = null;
+ private $sendmailAdditionalHeaders = null;
private $sendmailAdditionalArgs = null;
-
+
/**
* @return Mail
**/
@@ -114,6 +115,10 @@ public function send()
$headers .= "Content-Transfer-Encoding: 8bit\n";
$headers .= "Date: ".date('r')."\n";
+
+ if($this->sendmailAdditionalHeaders != null) {
+ $headers .= $this->sendmailAdditionalHeaders."\n";
+ }
if (
!mail(
@@ -221,5 +226,20 @@ public function setReturnPath($returnPath)
$this->returnPath = $returnPath;
return $this;
}
+
+ public function getSendmailAdditionalHeaders()
+ {
+ return $this->sendmailAdditionalHeaders;
+ }
+
+ /**
+ * @return Mail
+ **/
+ public function setSendmailAdditionalHeaders($sendmailAdditionalHeaders)
+ {
+ $this->sendmailAdditionalHeaders = $sendmailAdditionalHeaders;
+ return $this;
+ }
+
}
?>
\ No newline at end of file
diff --git a/main/Net/Mail/MailAddress.class.php b/main/Net/Mail/MailAddress.class.php
old mode 100644
new mode 100755
diff --git a/main/Net/Mail/MailBuilder.class.php b/main/Net/Mail/MailBuilder.class.php
old mode 100644
new mode 100755
diff --git a/main/Net/Mail/MailEncoding.class.php b/main/Net/Mail/MailEncoding.class.php
old mode 100644
new mode 100755
diff --git a/main/Net/Mail/MailException.class.php b/main/Net/Mail/MailException.class.php
old mode 100644
new mode 100755
diff --git a/main/Net/Mail/MailNotSentException.class.php b/main/Net/Mail/MailNotSentException.class.php
old mode 100644
new mode 100755
diff --git a/main/Net/Mail/MimeMail.class.php b/main/Net/Mail/MimeMail.class.php
old mode 100644
new mode 100755
index bfeb2caf9f..e034bde37b
--- a/main/Net/Mail/MimeMail.class.php
+++ b/main/Net/Mail/MimeMail.class.php
@@ -23,6 +23,8 @@ final class MimeMail implements MailBuilder
private $headers = null;
private $boundary = null;
+
+ private $contentType= null;
/**
* @return MimeMail
@@ -40,11 +42,11 @@ public function build()
throw new UnimplementedFeatureException();
if (!$this->boundary)
- $this->boundary = '=_'.md5(microtime(true));
-
+ $this->boundary = '=_'.md5(microtime(true) . uniqid());
+
$mail =
MimePart::create()->
- setContentType('multipart/mixed')->
+ setContentType(is_null($this->contentType)?'multipart/mixed':$this->contentType)->
setBoundary($this->boundary);
$this->headers =
@@ -57,7 +59,7 @@ public function build()
.$part->getHeaders()
."\n\n"
.$part->getEncodedBody()."\n";
-
+
$this->body .= '--'.$this->boundary."--"."\n\n";
}
@@ -93,5 +95,13 @@ public function getBoundary()
{
return $this->boundary;
}
+
+ public function setContentType($type) {
+ $this->contentType = $type;
+
+ return $this;
+ }
+
+
}
?>
\ No newline at end of file
diff --git a/main/Net/Mail/MimePart.class.php b/main/Net/Mail/MimePart.class.php
old mode 100644
new mode 100755
index f0e808b0c2..6413c81e8d
--- a/main/Net/Mail/MimePart.class.php
+++ b/main/Net/Mail/MimePart.class.php
@@ -19,7 +19,7 @@ final class MimePart implements MailBuilder
private $contentId = null;
private $contentType = null;
private $boundary = null;
-
+
private $encoding = null;
private $charset = null;
@@ -63,7 +63,7 @@ public function getBoundary()
{
return $this->boundary;
}
-
+
public function getContentId()
{
return $this->contentId;
@@ -223,10 +223,19 @@ public function getEncodedBody()
**/
case MailEncoding::QUOTED:
+// $string =
+// preg_replace(
+// '/[^\x21-\x3C\x3E-\x7E\x09\x20]/e',
+// 'sprintf("=%02x", ord ("$0"));',
+// $this->body
+// );
$string =
- preg_replace(
- '/[^\x21-\x3C\x3E-\x7E\x09\x20]/e',
- 'sprintf("=%02x", ord ("$0"));',
+ preg_replace_callback(
+ '/[^\x21-\x3C\x3E-\x7E\x09\x20]/',
+ function($value){
+ $symbol = array_shift($value);
+ return sprintf("=%02x", ord($symbol));
+ },
$this->body
);
@@ -254,14 +263,35 @@ public function getEncodedBody()
default:
throw new WrongStateException('unknown mail encoding given');
}
-
+
+ if ($this->parts) {
+ if (!$this->boundary) {
+ throw new UnexpectedValueException('set boundary or call getHeader() first');
+ }
+
+ foreach ($this->parts as $part) {
+ /** @var $part MimePart */
+ $body .=
+ '--' . $this->boundary . "\n"
+ . $part->getHeaders() . "\n\n"
+ . $part->getEncodedBody() . "\n\n"
+ ;
+ }
+
+ $this->body .= '--'.$this->boundary."--"."\n";
+ }
+
return $body;
}
-
+
public function getHeaders()
{
$headers = array();
-
+
+ if ($this->parts && !$this->boundary) {
+ $this->boundary = '=_'.md5(microtime(true) . uniqid());
+ }
+
if ($this->contentType) {
$header =
"Content-Type: {$this->contentType};";
@@ -271,7 +301,7 @@ public function getHeaders()
if ($this->boundary)
$header .= "\n\tboundary=\"{$this->boundary}\"";
-
+
$headers[] = $header;
}
diff --git a/main/Net/Mail/SmtpTransport.class.php b/main/Net/Mail/SmtpTransport.class.php
new file mode 100644
index 0000000000..7bb61782e1
--- /dev/null
+++ b/main/Net/Mail/SmtpTransport.class.php
@@ -0,0 +1,906 @@
+
+ * @date 2013.07.17
+ */
+
+class SmtpTransport {
+
+ const DEFAULT_PORT = 25;
+
+ const DEBUG_DISABLED = 0;
+ const DEBUG_MINIMUM = 10;
+ const DEBUG_MEDIUM = 20;
+ const DEBUG_MAXIMUM = 30;
+ const DEBUG_TOTAL = 40;
+
+ protected $useSSL = false;
+
+ protected $useTLS = false;
+
+ /** @var string */
+ protected $serverName = 'localhost';
+
+ /** @var string */
+ protected $authMethod = 'LOGIN';
+
+ /** @var int */
+ protected $streamTimeout = 30;
+
+ /** @var int */
+ protected $operationTimeLimit = 15;
+
+ /** @var string */
+ protected $crlf = "\r\n";
+
+ /** @var bool */
+ protected $useVerp = false;
+
+ /** @var int */
+ protected $debugLevel = 0;
+
+ /** @var Closure */
+ protected $debugWriter = null;
+
+ /** @var resource */
+ protected $connection = null;
+
+ /** @var string */
+ protected $serverAuthMethods = null;
+
+ /** @var string */
+ protected $lastRecipient = null;
+
+ /**
+ * @return SmtpTransport
+ */
+ public static function create() {
+ return new self;
+ }
+
+ protected function __construct() {
+ $this->debugWriter = function($message){
+ echo $message."\n";
+ };
+ }
+
+ /**
+ * @return boolean
+ */
+ public function isUseSSL() {
+ return $this->useSSL;
+ }
+
+ /**
+ * @param boolean $useSsl
+ * @return SmtpTransport
+ */
+ public function setUseSSL($useSsl) {
+ $this->useSSL = $useSsl;
+ return $this;
+ }
+
+ /**
+ * @return boolean
+ */
+ public function isUseTLS() {
+ return $this->useTLS;
+ }
+
+ /**
+ * @param boolean $useTls
+ * @return SmtpTransport
+ */
+ public function setUseTLS($useTls) {
+ $this->useTLS = $useTls;
+ return $this;
+ }
+
+ /**
+ * @return string
+ */
+ public function getServerName() {
+ return $this->serverName;
+ }
+
+ /**
+ * @param string $serverName
+ * @return SmtpTransport
+ */
+ public function setServerName($serverName) {
+ $this->serverName = $serverName;
+ return $this;
+ }
+
+ /**
+ * @return SmtpTransport
+ */
+ public function setAuthMethodPlain() {
+ $this->authMethod = 'PLAIN';
+ return $this;
+ }
+
+ /**
+ * @return SmtpTransport
+ */
+ public function setAuthMethodLogin() {
+ $this->authMethod = 'LOGIN';
+ return $this;
+ }
+
+ /**
+ * @return SmtpTransport
+ */
+ public function setAuthMethodCramMD5() {
+ $this->authMethod = 'CRAM-MD5';
+ return $this;
+ }
+
+ /**
+ * @return int
+ */
+ public function getStreamTimeout() {
+ return $this->streamTimeout;
+ }
+
+ /**
+ * Sets the SMTP timeout value for socket, in seconds
+ * @param int $streamTimeout
+ * @return SmtpTransport
+ */
+ public function setStreamTimeout($streamTimeout) {
+ $this->streamTimeout = $streamTimeout;
+ return $this;
+ }
+
+ /**
+ * @return int
+ */
+ public function getOperationTimeLimit() {
+ return $this->operationTimeLimit;
+ }
+
+ /**
+ * Sets the SMTP timelimit value for reads, in seconds
+ * @param int $operationTimeLimit
+ * @return SmtpTransport
+ */
+ public function setOperationTimeLimit($operationTimeLimit) {
+ $this->operationTimeLimit = $operationTimeLimit;
+ return $this;
+
+ }
+
+ /**
+ * @return string
+ */
+ public function getCrlf() {
+ return $this->crlf;
+ }
+
+ /**
+ * SMTP reply line ending
+ * @param string $crlf
+ * @return SmtpTransport
+ */
+ public function setCrlf($crlf) {
+ $this->crlf = $crlf;
+ return $this;
+ }
+
+ /**
+ * @return boolean
+ */
+ public function isUseVerp() {
+ return (bool)$this->useVerp;
+ }
+
+ /**
+ * Sets VERP use on/off
+ * @param boolean $useVerp
+ * @return SmtpTransport
+ */
+ public function setUseVerp($useVerp) {
+ $this->useVerp = $useVerp;
+ return $this;
+ }
+
+ /**
+ * @return SmtpTransport
+ */
+ public function debugDisabled() {
+ $this->debugLevel = self::DEBUG_DISABLED;
+ return $this;
+ }
+
+ /**
+ * @return SmtpTransport
+ */
+ public function debugMinimum() {
+ $this->debugLevel = self::DEBUG_MINIMUM;
+ return $this;
+ }
+
+ /**
+ * @return SmtpTransport
+ */
+ public function debugMedium() {
+ $this->debugLevel = self::DEBUG_MEDIUM;
+ return $this;
+ }
+
+ /**
+ * @return SmtpTransport
+ */
+ public function debugMaximum() {
+ $this->debugLevel = self::DEBUG_MAXIMUM;
+ return $this;
+ }
+
+ /**
+ * @return SmtpTransport
+ */
+ public function debugTotal() {
+ $this->debugLevel = self::DEBUG_TOTAL;
+ return $this;
+ }
+
+ /**
+ * @return callable
+ */
+ public function getDebugWriter() {
+ return $this->debugWriter;
+ }
+
+ /**
+ * @param callable $debugWriter
+ * @return SmtpTransport
+ */
+ public function setDebugWriter($debugWriter) {
+ $this->debugWriter = $debugWriter;
+ return $this;
+ }
+
+// public function sendMail(Mail $mail) {
+//
+// }
+//
+// public function sendMimeMail(MimeMail $mail) {
+//
+// }
+//
+// public function sendRawMail($to, $subject, $message, $additional_headers = null, $additional_parameters = null) {
+//
+// }
+
+ //===== common functions
+
+ /**
+ * Connect to an SMTP server
+ *
+ * SMTP CODE SUCCESS: 220
+ * SMTP CODE FAILURE: 421
+ * @param string $host SMTP server IP or host name
+ * @param int $port The port number to connect to, or use the default port if not specified
+ * @param array $options An array of options compatible with stream_context_create()
+ * @throws SmtpTransportException
+ * @return SmtpTransport
+ */
+ public function connect($host, $port = null, $options = array()) {
+ // Make sure we are __not__ connected
+ if($this->isConnected()) {
+ return $this;
+ }
+
+ if(empty($port)) {
+ $port = self::DEFAULT_PORT;
+ }
+
+ if($this->isUseSsl()) {
+ $host = 'ssl://'.$host;
+ }
+
+ // Connect to the SMTP server
+ $errno = 0;
+ $errstr = '';
+ $socket_context = stream_context_create($options);
+ //Need to suppress errors here as connection failures can be handled at a higher level
+ $this->connection = @stream_socket_client($host.":".$port, $errno, $errstr, $this->streamTimeout, STREAM_CLIENT_CONNECT, $socket_context);
+
+ // Verify we connected properly
+ if(empty($this->connection)) {
+ $this->debugOut("SMTP->ERROR: Failed to connect to server: $errstr ($errno)", self::DEBUG_MINIMUM);
+ throw new SmtpTransportException('Failed to connect to server');
+ }
+
+ // SMTP server can take longer to respond, give longer timeout for first read
+ // Windows does not have support for this timeout function
+ if(substr(PHP_OS, 0, 3) != 'WIN') {
+ $max = ini_get('max_execution_time');
+ if ($max != 0 && $this->streamTimeout > $max) { // Don't bother if unlimited
+ @set_time_limit($this->streamTimeout);
+ }
+ stream_set_timeout($this->connection, $this->streamTimeout, 0);
+ }
+
+ // get any announcement
+ $announce = $this->execCommand(null, 220);
+
+ $this->debugOut('SMTP: connected :: ' . $announce['payload'], self::DEBUG_MINIMUM);
+
+ return $this;
+ }
+
+ /**
+ * Sends the HELO command to the smtp server.
+ * This makes sure that we and the server are in
+ * the same known state.
+ *
+ * Implements from rfc 821: HELO
+ *
+ * SMTP CODE SUCCESS: 250
+ * SMTP CODE ERROR : 500, 501, 504, 421
+ *
+ * @throws SmtpTransportException
+ * @throws WrongStateException
+ * @return SmtpTransport
+ */
+ public function hello() {
+ $this->assertConnection(__METHOD__);
+
+ // if hostname for HELO was not specified send default
+ if(empty($this->serverName)) {
+ // determine appropriate default to send to server
+ $this->serverName = 'localhost';
+ }
+
+ $helloSent = false;
+
+ // Send extended hello first (RFC 2821)
+ try {
+ $hello = $this->execCommand('EHLO ' . $this->serverName, 250);
+ $helloSent = true;
+ } catch(SmtpTransportException $e) {
+ // skip...
+ }
+ // Send common hello
+ if( !$helloSent ) {
+ try {
+ $hello = $this->execCommand('HELO ' . $this->serverName, 250);
+ $helloSent = true;
+ } catch(SmtpTransportException $e) {
+ // skip...
+ }
+ }
+ // Check hello result
+ if( !$helloSent ) {
+ $this->debugOut("SMTP->ERROR: {$hello['code']}: {$hello['payload']}");
+ throw new SmtpTransportException('EHLO/HELO not accepted');
+ }
+ // Parse available auth methods
+ $authString = substr($hello['payload'], 5, strpos($hello['payload'], "\n")-6);
+ $this->serverAuthMethods = explode(' ', $authString);
+
+ $this->debugOut('SMTP: hello passed', self::DEBUG_MEDIUM);
+
+ return $this;
+ }
+
+ /**
+ * Initiate a TLS communication with the server.
+ *
+ * SMTP CODE 220 Ready to start TLS
+ * SMTP CODE 501 Syntax error (no parameters allowed)
+ * SMTP CODE 454 TLS not available due to temporary reason
+ * @throws SmtpTransportException
+ * @throws WrongStateException
+ * @return SmtpTransport
+ */
+ public function startTLS() {
+ $this->assertConnection(__METHOD__);
+
+ $initCrypto = false;
+ try {
+ $this->execCommand('STARTTLS', 220);
+ $initCrypto = true;
+ } catch(SmtpTransportException $e) {
+ // skip...
+ }
+
+ if( $initCrypto ) {
+ // Begin encrypted connection
+ if(!stream_socket_enable_crypto($this->connection, true, STREAM_CRYPTO_METHOD_TLS_CLIENT)) {
+ throw new SmtpTransportException('Could not init TLS on socket level');
+ }
+ } else {
+ $this->debugOut('STARTTLS not accepted from server', self::DEBUG_MEDIUM);
+ }
+
+ $this->debugOut('SMTP: StarTLS started', self::DEBUG_MEDIUM);
+
+ return $this;
+ }
+
+ /**
+ * Performs SMTP authentication. Must be run after running the
+ * Hello() method.
+ * @param string $username
+ * @param string $password
+ * @throws SmtpTransportException
+ * @throws WrongStateException
+ * @return SmtpTransport
+ */
+ public function authenticate($username, $password) {
+ $this->assertConnection(__METHOD__);
+
+ if( !in_array($this->authMethod, $this->serverAuthMethods) ) {
+ throw new WrongStateException("Auth method '{$this->authMethod}' is not supported by server; use: ".implode(', ', $this->serverAuthMethods));
+ }
+ // Start authentication
+ switch ($this->authMethod) {
+ case 'PLAIN': {
+ try {
+ $this->execCommand('AUTH '.$this->authMethod.' '.base64_encode("\0".$username."\0".$password), 235);
+ } catch(SmtpTransportException $e) {
+ throw new SmtpTransportException('Authentication not accepted from server', $e->getCode(), $e);
+ }
+ } break;
+ case 'LOGIN': {
+ try {
+ $this->execCommand('AUTH '.$this->authMethod, 334);
+ $this->execCommand(base64_encode($username), 334);
+ $this->execCommand(base64_encode($password), 235);
+ } catch(SmtpTransportException $e) {
+ throw new SmtpTransportException('Authentication not accepted from server', $e->getCode(), $e);
+ }
+ } break;
+ case 'CRAM-MD5': {
+ try {
+ $reply = $this->execCommand('AUTH '.$this->authMethod, 334);
+ // Get the challenge
+ $challenge = base64_decode($reply['payload']);
+ // Build the response
+ $response = base64_encode( $username . ' ' . $this->hmac($challenge, $password) );
+ $this->execCommand($response, 235);
+ } catch(SmtpTransportException $e) {
+ throw new SmtpTransportException('Authentication not accepted from server', $e->getCode(), $e);
+ }
+ } break;
+ }
+
+ $this->debugOut('SMTP: Authentication passed', self::DEBUG_MINIMUM);
+
+ return $this;
+ }
+
+ /**
+ * Starts a mail transaction from the email address specified in
+ * $from. Returns true if successful or false otherwise. If True
+ * the mail transaction is started and then one or more Recipient
+ * commands may be called followed by a Data command.
+ *
+ * Implements rfc 821: MAIL FROM:
+ *
+ * SMTP CODE SUCCESS: 250
+ * SMTP CODE SUCCESS: 552, 451, 452
+ * SMTP CODE SUCCESS: 500, 501, 421
+ * @param string $from
+ * @throws SmtpTransportException
+ * @throws WrongArgumentException
+ * @throws WrongStateException
+ * @return SmtpTransport
+ */
+ public function startMail($from) {
+ $this->assertConnection(__METHOD__);
+ $this->assertValidEmail($from);
+
+ $command = 'MAIL FROM:<' . $from . '>';
+ if( $this->isUseVerp() ) {
+ $command .= ' XVERP';
+ }
+ try {
+ $this->execCommand($command, 250);
+ } catch(SmtpTransportException $e) {
+ throw new SmtpTransportException('MAIL not accepted from server', $e->getCode(), $e);
+ }
+
+ $this->debugOut('SMTP: mail from :: ' . $from, self::DEBUG_MEDIUM);
+
+ return $this;
+ }
+
+ /**
+ * Sends the command RCPT to the SMTP server with the TO: argument of $to.
+ * Returns true if the recipient was accepted false if it was rejected.
+ *
+ * Implements from rfc 821: RCPT TO:
+ *
+ * SMTP CODE SUCCESS: 250, 251
+ * SMTP CODE FAILURE: 550, 551, 552, 553, 450, 451, 452
+ * SMTP CODE ERROR : 500, 501, 503, 421
+ * @param string $to
+ * @throws SmtpTransportException
+ * @throws WrongArgumentException
+ * @throws WrongStateException
+ * @return SmtpTransport
+ */
+ public function setRecipient($to) {
+ $this->assertConnection(__METHOD__);
+ $this->assertValidEmail($to);
+
+ try {
+ $this->execCommand('RCPT TO:<' . $to . '>', 250);
+ } catch(SmtpTransportException $e) {
+ throw new SmtpTransportException('RCPT not accepted from server', $e->getCode(), $e);
+ }
+
+ $this->lastRecipient = $to;
+ $this->debugOut('SMTP: rctp to :: ' . $to, self::DEBUG_MEDIUM);
+
+ return $this;
+ }
+
+ /**
+ * Issues a data command and sends the msg_data to the server
+ * finializing the mail transaction. $msg_data is the message
+ * that is to be send with the headers. Each header needs to be
+ * on a single line followed by a with the message headers
+ * and the message body being separated by and additional .
+ *
+ * Implements rfc 821: DATA
+ *
+ * SMTP CODE INTERMEDIATE: 354
+ * [data]
+ * .
+ * SMTP CODE SUCCESS: 250
+ * SMTP CODE FAILURE: 552, 554, 451, 452
+ * SMTP CODE FAILURE: 451, 554
+ * SMTP CODE ERROR : 500, 501, 503, 421
+ * @param string $data
+ * @throws SmtpTransportException
+ * @throws MailNotSentException
+ * @throws WrongArgumentException
+ * @return SmtpTransport
+ */
+ public function setContent($data) {
+ $this->assertConnection(__METHOD__);
+
+ try {
+ $this->execCommand('DATA', 354);
+ } catch(SmtpTransportException $e) {
+ throw new SmtpTransportException('DATA not accepted from server', $e->getCode(), $e);
+ }
+
+ /* the server is ready to accept data!
+ * according to rfc 821 we should not send more than 1000
+ * including the CRLF
+ * characters on a single line so we will break the data up
+ * into lines by \r and/or \n then if needed we will break
+ * each of those into smaller lines to fit within the limit.
+ * in addition we will be looking for lines that start with
+ * a period '.' and append and additional period '.' to that
+ * line. NOTE: this does not count towards limit.
+ */
+
+ // normalize the line breaks so we know the explode works
+ $data = str_replace(array("\r\n", "\r"), "\n", $data);
+ $lines = explode("\n", $data);
+
+
+ $field = substr($lines[0], 0, strpos($lines[0], ':'));
+ $in_headers = false;
+ if(!empty($field) && !strstr($field, ' ')) {
+ $in_headers = true;
+ }
+
+ $max_line_length = 998; // used below; set here for ease in change
+
+ while(list(, $line) = @each($lines)) {
+ $lines_out = null;
+ if($line == '' && $in_headers) {
+ $in_headers = false;
+ }
+ // ok we need to break this line up into several smaller lines
+ while(strlen($line) > $max_line_length) {
+ $pos = strrpos(substr($line, 0, $max_line_length), ' ');
+
+ // Patch to fix DOS attack
+ if(!$pos) {
+ $pos = $max_line_length - 1;
+ $lines_out[] = substr($line, 0, $pos);
+ $line = substr($line, $pos);
+ } else {
+ $lines_out[] = substr($line, 0, $pos);
+ $line = substr($line, $pos + 1);
+ }
+
+ /* if processing headers add a LWSP-char to the front of new line
+ * rfc 822 on long msg headers
+ */
+ if($in_headers) {
+ $line = "\t" . $line;
+ }
+ }
+ $lines_out[] = $line;
+
+ // send the lines to the server
+ while(list(, $line_out) = @each($lines_out)) {
+ if(strlen($line_out) > 0)
+ {
+ if(substr($line_out, 0, 1) == '.') {
+ $line_out = '.' . $line_out;
+ }
+ }
+ $this->execCommand($line_out, 250);
+ }
+ }
+
+ // message data has been sent
+ try {
+ $this->execCommand('.', 251);
+ } catch(SmtpTransportException $e) {
+ throw new MailNotSentException($e->getMessage(), $e->getCode());
+ }
+
+ $this->debugOut('SMTP: mail sent to :: '.$this->lastRecipient, self::DEBUG_MINIMUM);
+
+ return $this;
+ }
+
+ /**
+ * Sends the RSET command to abort and transaction that is
+ * currently in progress. Returns true if successful false
+ * otherwise.
+ *
+ * Implements rfc 821: RSET
+ *
+ * SMTP CODE SUCCESS: 250
+ * SMTP CODE ERROR : 500, 501, 504, 421
+ * @throws SmtpTransportException
+ * @throws WrongStateException
+ * @return SmtpTransport
+ */
+ public function reset() {
+ $this->assertConnection(__METHOD__);
+
+ // clear last recipient
+ $this->lastRecipient = null;
+
+ // send the quit command to the server
+ try {
+// $this->execCommand('RSET', 250);
+ } catch(SmtpTransportException $e) {
+ throw new SmtpTransportException('RSET not accepted from server', $e->getCode(), $e);
+ }
+
+ return $this;
+ }
+
+ /**
+ * Sends the quit command to the server and then closes the socket
+ * if there is no error or the $close_on_error argument is true.
+ *
+ * Implements from rfc 821: QUIT
+ *
+ * SMTP CODE SUCCESS: 221
+ * SMTP CODE ERROR : 500
+ * @throws SmtpTransportException
+ * @throws WrongStateException
+ * @return SmtpTransport
+ */
+ public function quit() {
+ $this->assertConnection(__METHOD__);
+
+ // send the quit command to the server
+ try {
+ $this->execCommand('QUIT', 221);
+ } catch(SmtpTransportException $e) {
+ throw new SmtpTransportException('QUIT not accepted from server', $e->getCode(), $e);
+ }
+
+ $this->debugOut('SMTP: quited', self::DEBUG_MINIMUM);
+
+ return $this;
+ }
+
+ /**
+ * Closes the socket and cleans up the state of the class.
+ * It is not considered good to use this function without
+ * first trying to use QUIT.
+ * @return SmtpTransport
+ */
+ public function close() {
+ $this->error = null; // so there is no confusion
+ $this->helo_rply = null;
+ if(!empty($this->connection)) {
+ // close the connection and cleanup
+ fclose($this->connection);
+ $this->connection = null;
+ }
+
+ $this->debugOut('SMTP: connection closed', self::DEBUG_MINIMUM);
+
+ return $this;
+ }
+
+ /**
+ * Returns true if connected to a server otherwise false
+ * @return bool
+ */
+ public function isConnected() {
+ if(!empty($this->connection)) {
+ $sock_status = stream_get_meta_data($this->connection);
+ if($sock_status['eof']) {
+ // the socket is valid but we are not connected
+ $this->debugOut('SMTP->NOTICE: EOF caught while checking if connected', self::DEBUG_TOTAL);
+ $this->close();
+ return false;
+ }
+ return true; // everything looks good
+ }
+ return false;
+ }
+
+ /**
+ * Assert established connection
+ * @param string $method
+ * @throws WrongStateException
+ */
+ protected function assertConnection($method) {
+ if(!$this->isConnected()) {
+ throw new WrongStateException("Called $method() without being connected");
+ }
+ }
+
+ /**
+ * Assert valid email address
+ * @param string $email
+ * @throws WrongArgumentException
+ */
+ protected function assertValidEmail($email) {
+ $form =
+ Form::create()
+ ->add(
+ Primitive::string('email')
+ ->setAllowedPattern(PrimitiveString::MAIL_PATTERN)
+ ->setMin(6)
+ ->setMax(1024)
+ ->required()
+ )
+ ->import(
+ array('email'=>$email)
+ );
+ if( $form->getErrors() ) {
+ throw new WrongArgumentException("Provided string \"{$email}\" is not valid email");
+ }
+ }
+
+ //===== internal functions
+
+ protected function execCommand($command, $expectedCode) {
+ if( !is_null($command) ) {
+ $this->rawPut($command . $this->crlf);
+ $this->debugOut('', self::DEBUG_MAXIMUM);
+ $this->debugOut("Send command {$command}", self::DEBUG_MAXIMUM);
+ }
+ $reply = trim($this->rawGet());
+
+ $code = intval( trim(substr($reply, 0, 4)) );
+ $payload = substr($reply, 4);
+
+ $this->debugOut("Got answer with code {$code} and payload: {$payload}", self::DEBUG_MAXIMUM);
+ if( $code!=$expectedCode ) {
+ throw new SmtpTransportException("Wrong answer code got {$code} but {$expectedCode} expected; last command: {$command}");
+ }
+
+ return array(
+ 'code' => $code,
+ 'payload' => $payload,
+ );
+ }
+
+ /**
+ * Read in as many lines as possible
+ * either before eof or socket timeout occurs on the operation.
+ * With SMTP we can tell if we have more lines to read if the
+ * 4th character is '-' symbol. If it is a space then we don't
+ * need to read anything else.
+ * @throws NetworkException
+ * @return string
+ */
+ protected function rawGet() {
+ $data = '';
+ $endtime = 0;
+ /* If for some reason the fp is bad, don't inf loop */
+ if (!is_resource($this->connection)) {
+ return $data;
+ }
+ stream_set_timeout($this->connection, $this->streamTimeout);
+ if ($this->operationTimeLimit > 0) {
+ $endtime = time() + $this->operationTimeLimit;
+ }
+ while(is_resource($this->connection) && !feof($this->connection)) {
+ $str = @fgets($this->connection, 515);
+ $data .= $str;
+ // if 4th character is a space, we are done reading, break the loop
+ if(substr($str, 3, 1) == ' ') { break; }
+ // Timed-out? Log and break
+ $info = stream_get_meta_data($this->connection);
+ if ($info['timed_out']) {
+ $message = "SMTP->rawGet(): timed-out ({$this->streamTimeout} seconds)";
+ $this->debugOut($message, self::DEBUG_MINIMUM);
+ throw new NetworkException($message);
+ }
+ // Now check if reads took too long
+ if ($endtime) {
+ if (time() > $endtime) {
+ $message = "SMTP->rawGet(): timelimit reached ({$this->operationTimeLimit} seconds)";
+ $this->debugOut($message, self::DEBUG_MINIMUM);
+ throw new NetworkException($message);
+ }
+ }
+ }
+ $this->debugOut("SMTP get data: \"$data\"", self::DEBUG_TOTAL);
+ return $data;
+ }
+
+ /**
+ * Sends data to the server
+ * Returns number of bytes sent to the server or FALSE on error
+ * @param string $data
+ * @return int
+ */
+ protected function rawPut($data) {
+ $this->debugOut("SMTP put data: $data", self::DEBUG_TOTAL);
+ return fwrite($this->connection, $data);
+ }
+
+ /**
+ * Works like hash_hmac('md5', $data, $key) in case that function is not available
+ * @access protected
+ * @param string $data
+ * @param string $key
+ * @return string
+ */
+ protected function hmac($data, $key) {
+ if (function_exists('hash_hmac')) {
+ return hash_hmac('md5', $data, $key);
+ }
+
+ // The following borrowed from http://php.net/manual/en/function.mhash.php#27225
+
+ // RFC 2104 HMAC implementation for php.
+ // Creates an md5 HMAC.
+ // Eliminates the need to install mhash to compute a HMAC
+ // Hacked by Lance Rushing
+
+ $b = 64; // byte length for md5
+ if (strlen($key) > $b) {
+ $key = pack('H*', md5($key));
+ }
+ $key = str_pad($key, $b, chr(0x00));
+ $ipad = str_pad('', $b, chr(0x36));
+ $opad = str_pad('', $b, chr(0x5c));
+ $k_ipad = $key ^ $ipad ;
+ $k_opad = $key ^ $opad;
+
+ return md5($k_opad . pack('H*', md5($k_ipad . $data)));
+ }
+
+ /**
+ * @param string $message
+ * @param int $minLevel
+ * @throws WrongStateException
+ */
+ protected function debugOut($message, $minLevel = 100) {
+ if( $this->debugLevel < $minLevel ) {
+ return;
+ }
+ if( !is_callable($this->debugWriter) ) {
+ throw new WrongStateException('Debug writer is not callable!');
+ }
+ call_user_func($this->debugWriter, $message);
+ }
+
+}
\ No newline at end of file
diff --git a/main/Net/Mail/SmtpTransportException.class.php b/main/Net/Mail/SmtpTransportException.class.php
new file mode 100644
index 0000000000..0b548d4ce9
--- /dev/null
+++ b/main/Net/Mail/SmtpTransportException.class.php
@@ -0,0 +1,15 @@
+ctpp = new ctpp();
+ }
+
+ /**
+ * @param string $templateName
+ * @return CtppView
+ */
+ public function setTemplateName($templateName) {
+ $this->templateName = $templateName;
+ return $this;
+ }
+
+ /**
+ * @param array $templatePaths
+ * @return CtppView
+ */
+ public function setTemplatePaths(array $templatePaths) {
+ $this->templatePaths = $templatePaths;
+ return $this;
+ }
+
+ /**
+ * @param string $templatePath
+ * @return CtppView
+ */
+ public function addTemplatePath($templatePath) {
+ $this->templatePaths[] = $templatePath;
+ return $this;
+ }
+
+ /**
+ * @param string $templateExt
+ * @return CtppView
+ */
+ public function setTemplateExt($templateExt) {
+ $this->templateExt = $templateExt;
+ return $this;
+ }
+
+ /**
+ * @param int $steps_limit
+ * @return CtppView
+ */
+ public function setStepsLimit($steps_limit) {
+ if( is_int($steps_limit) ) {
+ $this->steps_limit = $steps_limit;
+ } else {
+ throw new WrongArgumentException('Steps limit nust be integer!');
+ }
+
+ return $this;
+ }
+
+ /**
+ * @param Model
+ * @return mixed
+ */
+ public function render(/* Model */ $model = null) {
+ $this->makeByteCode();
+ $this->setData( $model );
+ return $this->ctpp->output( $this->bytecode );
+ }
+
+ /**
+ * @param Model
+ * @return mixed
+ */
+ public function toString(/* Model */ $model = null) {
+ $this->makeByteCode();
+ $this->setData( $model );
+ $result = $this->ctpp->output_string( $this->bytecode );
+ if( $result === false ) {
+ throw new CtppException( $this->ctpp->get_last_error() );
+ }
+ return $result;
+ }
+
+ private function makeByteCode() {
+ if( empty($this->templatePaths) ) {
+ throw new WrongStateException('Template paths are not defined!');
+ }
+ if( empty($this->templateName) ) {
+ throw new WrongStateException('Template name is not defined!');
+ }
+ // TODO: прикрутить кэширование байткода
+ // making bytecode
+ $this->ctpp->include_dirs( $this->templatePaths );
+ $this->bytecode = $this->ctpp->parse_template( $this->templateName.'.'.$this->templateExt );
+ }
+
+ private function setData(Model $model = null) {
+ $this->ctpp->emit_params( $model->getList() );
+ }
+
+}
diff --git a/main/UI/View/DebugPhpView.class.php b/main/UI/View/DebugPhpView.class.php
old mode 100644
new mode 100755
diff --git a/main/UI/View/EmptyGifView.class.php b/main/UI/View/EmptyGifView.class.php
old mode 100644
new mode 100755
diff --git a/main/UI/View/EmptyView.class.php b/main/UI/View/EmptyView.class.php
old mode 100644
new mode 100755
diff --git a/main/UI/View/ExtendedPhpView.class.php b/main/UI/View/ExtendedPhpView.class.php
new file mode 100755
index 0000000000..15ca6d7b8a
--- /dev/null
+++ b/main/UI/View/ExtendedPhpView.class.php
@@ -0,0 +1,79 @@
+
+ * @date 2012.03.20
+ */
+class ExtendedPhpView extends SimplePhpView {
+
+ protected
+ /**
+ * @var PartViewer
+ */
+ $partViewer = null,
+ /**
+ * вьюшка, которую выпаолним до основной
+ * @var string
+ */
+ $preView = null,
+ /**
+ * вьюшка, которую выполним после основной
+ * @var string
+ */
+ $postView = null;
+
+ /**
+ * @param string $preView
+ * @return ExtendedPhpView
+ */
+ public function setPreView($preView) {
+ $this->preView = $preView;
+ return $this;
+ }
+
+ /**
+ * @param string $postView
+ * @return ExtendedPhpView
+ */
+ public function setPostView($postView) {
+ $this->postView = $postView;
+ return $this;
+ }
+
+ /**
+ * @param Model $model
+ * @return ExtendedPhpView
+ */
+ public function render(/* Model */ $model = null)
+ {
+ Assert::isTrue($model === null || $model instanceof Model);
+
+ if ($model)
+ extract($model->getList());
+
+ $this->partViewer = $partViewer = new PartViewer($this->partViewResolver, $model);
+
+ $this->preRender();
+
+ include $this->templatePath;
+
+ $this->postRender();
+
+ return $this;
+ }
+
+ protected function preRender() {
+ if( !is_null($this->preView) ) {
+ $this->partViewer->view($this->preView);
+ }
+ return $this;
+ }
+
+ protected function postRender() {
+ if( !is_null($this->postView) ) {
+ $this->partViewer->view($this->postView);
+ }
+ return $this;
+ }
+
+}
diff --git a/main/UI/View/ExtendedView.class.php b/main/UI/View/ExtendedView.class.php
new file mode 100644
index 0000000000..69bb3bf30b
--- /dev/null
+++ b/main/UI/View/ExtendedView.class.php
@@ -0,0 +1,62 @@
+
+ * @date 10.07.13
+ */
+
+class ExtendedView implements View {
+ /** @var View[] */
+ protected $before = array();
+ /** @var View[] */
+ protected $after = array();
+ /** @var View */
+ protected $body = null;
+
+ protected function __construct(View $body) {
+ $this->body = $body;
+ }
+
+ public static function create(View $body) {
+ return new static($body);
+ }
+
+ public function addHeader(View $view) {
+ $this->before []= $view;
+ return $this;
+ }
+
+ public function addFooter(View $view) {
+ $this->after []= $view;
+ return $this;
+ }
+
+ /**
+ * @param $model null or Model
+ * @return self
+ **/
+ public function render($model = null) {
+ // begin rendering
+ ob_start();
+
+ // render headers
+ foreach ($this->before as $pre) {
+ $pre->render($model);
+ }
+
+ // render body
+ $this->body->render($model);
+
+ // render footers
+ foreach ($this->after as $post) {
+ $post->render($model);
+ }
+
+ // done rendering
+ ob_end_flush();
+
+ return $this;
+ }
+
+
+}
\ No newline at end of file
diff --git a/main/UI/View/HttpErrorView.class.php b/main/UI/View/HttpErrorView.class.php
old mode 100644
new mode 100755
diff --git a/main/UI/View/JsonView.class.php b/main/UI/View/JsonView.class.php
index 3df47ca817..c2255188e6 100644
--- a/main/UI/View/JsonView.class.php
+++ b/main/UI/View/JsonView.class.php
@@ -15,6 +15,7 @@
final class JsonView implements View, Stringable
{
protected $options = 0;
+ protected $callback = null;
/**
* @return JsonView
@@ -147,13 +148,34 @@ public function setUnescapedSlashes($flag = false)
return $this;
}
+
+ /**
+ * создание callback
+ *
+ * @param $callback
+ * @return JsonView
+ */
+ public function setCallBack($callback)
+ {
+ $this->callback = $callback;
+
+ return $this;
+ }
/**
* @return JsonView
**/
public function render(/* Model */ $model = null)
{
- echo $this->toString($model);
+ if (!headers_sent()) {
+ header('Content-Type: ' . ($this->callback ? 'text/javascript' : 'application/json'));
+ }
+ if (is_null($this->callback)) {
+ echo $this->toString($model);
+ }
+ else {
+ echo $this->callback.'('.$this->toString($model).')';
+ }
return $this;
}
diff --git a/main/UI/View/MultiPrefixPhpViewResolver.class.php b/main/UI/View/MultiPrefixPhpViewResolver.class.php
old mode 100644
new mode 100755
diff --git a/main/UI/View/PartViewer.class.php b/main/UI/View/PartViewer.class.php
old mode 100644
new mode 100755
diff --git a/main/UI/View/PhpViewResolver.class.php b/main/UI/View/PhpViewResolver.class.php
old mode 100644
new mode 100755
diff --git a/main/UI/View/RedirectToView.class.php b/main/UI/View/RedirectToView.class.php
old mode 100644
new mode 100755
diff --git a/main/UI/View/RedirectView.class.php b/main/UI/View/RedirectView.class.php
old mode 100644
new mode 100755
diff --git a/main/UI/View/SimplePhpView.class.php b/main/UI/View/SimplePhpView.class.php
old mode 100644
new mode 100755
diff --git a/main/UI/View/View.class.php b/main/UI/View/View.class.php
old mode 100644
new mode 100755
diff --git a/main/UI/View/ViewResolver.class.php b/main/UI/View/ViewResolver.class.php
old mode 100644
new mode 100755
diff --git a/main/UI/Widget/BaseWidget.class.php b/main/UI/Widget/BaseWidget.class.php
new file mode 100755
index 0000000000..aaf982bada
--- /dev/null
+++ b/main/UI/Widget/BaseWidget.class.php
@@ -0,0 +1,162 @@
+
+ * @date 2012.03.11
+ */
+abstract class BaseWidget implements IWidget
+{
+ protected $name = null;
+ protected $templatePath = null;
+ protected $templateName = null;
+ protected $list = null;
+
+ protected static $templatePathPrefixes = array();
+ protected static $viewer = null;
+
+ /**
+ * @var PartViewer
+ */
+
+ public function __construct($name = null)
+ {
+ $this->name = $name;
+ }
+
+ protected static function getViewer() {
+ if (!self::$viewer) {
+ $viewResolver = MultiPrefixPhpViewResolver::create()
+ ->setViewClassName('SimplePhpView');
+
+ foreach (self::$templatePathPrefixes as $prefix) {
+ $viewResolver->addPrefix($prefix);
+ }
+ self::$viewer = new PartViewer($viewResolver, Model::create());
+ }
+ return self::$viewer;
+ }
+
+ /**
+ * @param PartViewer $viewer
+ * @return $this
+ */
+ public static function setViewer(PartViewer $viewer)
+ {
+ self::$viewer = $viewer;
+ }
+
+
+ public static function addTemplatePrefix($prefix) {
+ self::$templatePathPrefixes []= $prefix;
+ self::$viewer = null;
+ }
+
+ /**
+ * @param string $key
+ * @param mixed $value
+ * @return BaseWidget
+ */
+ public function set($key, $value)
+ {
+ if( ( mb_substr($key, 0, 1) != '_' ) )
+ $key = '_'.$key;
+
+ $this->list[$key] = $value;
+
+
+ return $this;
+ }
+
+ /**
+ * @return BaseWidget
+ */
+ public function setTemplateName($value)
+ {
+ $this->templateName = $value;
+
+ return $this;
+ }
+
+ /**
+ * @return string
+ */
+ public function getTemplateName()
+ {
+ return $this->templateName;
+ }
+
+ /**
+ * @return Model
+ */
+ protected function makeModel()
+ {
+ $model = $this->getViewer()->getModel();
+ $model->set('_name', $this->name);
+
+ if(
+ is_array( $this->list ) &&
+ count( $this->list )
+ ){
+ foreach ( $this->list as $key => $value ) {
+ $model->set($key, $value);
+ }
+ }
+
+ return $model;
+ }
+
+ /** (non-PHPdoc)
+ * @see core/Base/Stringable::toString()
+ * @throws Exception
+ * @return string
+ */
+ public function toString()
+ {
+ try {
+ $this->makeModel();
+
+ ob_start();
+
+ $this->getViewer()->view(
+ $this->templatePath. DIRECTORY_SEPARATOR. $this->templateName
+ );
+
+ $source = ob_get_contents();
+
+ ob_end_clean();
+ }
+ catch (Exception $e) {
+ // FIXME
+ error_log(__METHOD__ . ': '.$e->__toString() );
+ throw $e;
+ }
+
+ return (string) $source;
+ }
+
+ /** (non-PHPdoc)
+ * @see main/Flow/View::render()
+ * @param Model|null $model
+ * @return void
+ */
+ public function render($model = null)
+ {
+ if( $model )
+ $this->getViewer()->
+ getModel()->
+ merge($model);
+
+ echo $this->toString();
+
+ return /*void*/;
+ }
+
+ /**
+ * @return string
+ */
+ public function __toString()
+ {
+ return $this->toString();
+ }
+}
+
diff --git a/main/UI/Widget/DataGrid.class.php b/main/UI/Widget/DataGrid.class.php
new file mode 100755
index 0000000000..a52cad5d97
--- /dev/null
+++ b/main/UI/Widget/DataGrid.class.php
@@ -0,0 +1,1045 @@
+
+ * @date 2012.03.19
+ */
+class DataGrid extends BaseWidget
+{
+ /** @var string имя таблицы */
+ protected $name = null;
+
+ /** @var DataGrid для вложенных таблиц */
+ protected $parent = null;
+
+ /** @var int предел вложенности */
+ protected static $maxNesting = 1;
+
+ /** @var array список дочерних объектов */
+ protected $objects = array();
+
+
+ /** @var array список 'fieldID' => 'Field name' */
+ protected $fields = array();
+
+ /** @var array список локализованных полей */
+ protected $localizedFields = null;
+
+ /** @var array список полей, по которым можно сортировать.
+ * Фактически, исключает поля, добавленные через addColumn */
+ protected $sortingFields = array();
+
+ /** @var array список 'fieldID' => callback() */
+ protected $renderers = array();
+
+ /** @var array массив строк таблицы */
+ protected $rows = array();
+
+ /** @var int ИД строки с "итого" */
+ protected $totalId = null;
+
+
+ /** @var array аттрибуты заголовков таблицы */
+ protected $fieldHtmlOptions = array();
+
+ /** @var array аттрибуты |