/** * Add one object into objects and its associated db to watch * @param epObject $o * @param epDbObject $db * @return bool */ public function addObject($o, $db) { // validate input if (!$o instanceof epObject) { return false; } // has object been kept? $uid = $o->epGetUId(); if (!isset($this->objects[$uid])) { // check if object is in transaction. start if not if (!$o->epInTransaction()) { $o->epStartTransaction(); } // add the associated db to watch $this->addDb($db); // add if not $this->objects[$uid] = $o; } return true; }
/** * Cache an object * @param epObject $o The object to be added into cache * @param boolean $force Whether to force replace object in cache * @return boolean */ public function add(epObject &$o, $force = false) { // don't cache objects that haven't been committed if (!($oid = $o->epGetObjectId())) { return false; } // get class if (!($class = $o->epGetClass())) { return false; } // generate key $key = $this->_key($class); // get all objects for the class if (!($os = $this->cache->get($key))) { $os = array(); } // done if not forced -and- oid already in cache if (!$force && in_array($o->oid, array_keys($os))) { return true; } // put object into array $os[$o->oid] = $o; // is class write locked? if ($this->isLocked($class, self::LOCK_WRITE)) { return false; } // lock write $this->_lock($class, self::LOCK_WRITE); // cache array $status = $this->cache->set($key, $os); // unlock write $this->_unlock($class, self::LOCK_WRITE); return $status; }
/** * test array access */ function testArrayAccess() { // make an eptTest object $this->assertTrue($o = new eptTest("XXX", "YYY", "ZZZ")); // wrap it with epObject $this->assertTrue($w = new epObject($o)); // test new object is not dirty $this->assertFalse($w->epIsDirty()); // test epGet('x') against direct object property access ($o->x) $this->assertTrue($w['x'] === $o->x); // test epSet('x'), epGet('x'), and $w->x (overloading __get()) $value = md5($w['x']); $this->assertTrue($w['x'] = $value); $this->assertTrue($o->x === $value); $this->assertTrue($w['x'] === $o->x); $this->assertTrue($w->epIsDirty()); // check if 'x' is in modified list $this->assertTrue($modified = $w->epGetModifiedVars()); $this->assertTrue(array_key_exists('x', $modified)); $this->assertTrue($modified['x'] === $value); // getting protected 'y' gets exception try { $this->assertTrue($w['y'] === false); } catch (Exception $e) { // !!!simpletest seem to ignore assert in catch block $this->assertTrue($e instanceof epExceptionObject); } // setting protected 'y' gets exception try { $w['y'] = 'YYY|YYY'; } catch (Exception $e) { // !!!simpletest seem to ignore assert in catch block $this->assertTrue($e instanceof epExceptionObject); } // getting protected 'z' gets exception try { $this->assertTrue($w['z'] === false); } catch (Exception $e) { // !!!simpletest seem to ignore assert in catch block $this->assertTrue($e instanceof epExceptionObject); } // setting protected 'z' gets exception try { $w['z'] = 'ZZZ|ZZZ'; } catch (Exception $e) { // !!!simpletest seem to ignore assert in catch block $this->assertTrue($e instanceof epExceptionObject); } // test foreach $vars = array(); foreach ($w as $var => $value) { $vars[$var] = $value; } // check var number $this->assertTrue(count($vars) == $w->count()); }
/** * Generate a string that can be used to uniquely identify an object. * False is returned if invalid object or object does not have oid yet. * @param epObject $o * @return false|string */ public function encodeUoid(epObject $o) { if (!$o || !($oid = $o->epGetObjectId())) { return false; } // put class name and id into simple format: "<class>:<id>" return $this->_encodeUoid($this->_getClass($o), $oid); }
/** * Checks if the variables of this object matches all the * non-null variables in another object * * <b> * Note: for now we only match primitive fields. For relational * fields, things can get complicated as we may be dealing with * very "deep" comparisons and recursions. * </b> * * @param epObject $o the example object * @param bool $deep should this be a deep search * @return bool */ public function epMatches($o, $deep = false) { // make sure param is epObject if (!$o instanceof epObject) { return false; } // same object if same object uid if ($this->ep_uid == $o->epGetUId()) { return true; } // matches if the example object does not have any non-null variable if (!($vars_o = $o->epGetVars())) { return true; } // get vars of this object $vars = $this->epGetVars(); // set 'matching' flag to avoid endless loop $this->epSetMatching(true); $o->epSetMatching(true); // go through each var from the example object foreach ($vars_o as $var => $value) { // skip 'oid' if ($var == 'oid') { continue; } // ignore null values (including empty string) if (is_null($value) || is_string($value) && !$value) { continue; } // for primitive var, mismatch if var is not in object or two vars unequal if ($this->epIsPrimitive($var)) { if (!isset($vars[$var]) || $vars[$var] != $value) { $this->epSetMatching(false); $o->epSetMatching(false); return false; } continue; } // done if no deep matching (see method comments) if (!$deep) { continue; } // deep matching for relationship fields // many-valued relationship var if ($value instanceof epArray) { // the epArray can have different order between the // two objects, so we have to do an n^2 loop to see // if they are the same (ugly) // oak: shall we match array count? strict mode? // foreach ($value as $obj) { if (!$obj) { continue; } // skip object already under matching if ($obj->epIsMatching()) { continue; } // flag indicates object being matched $matched = false; // go through each var in array foreach ($vars[$var] as $tmp) { if (!$tmp->epIsMatching() && !$obj->epIsMatching()) { if ($tmp->epMatches($obj, true)) { $matched = true; break; } } } // no match to $obj. done. if (!$matched) { $this->epSetMatching(false); $o->epSetMatching(false); return false; } } } else { if ($value instanceof epObject && !$value->epIsMatching()) { // we need to check this if (!isset($vars[$var])) { $vars[$var] = $this->epGet($var); } // match only when vars are not flagged 'matching' if (!$value->epIsMatching() && !$vars[$var]->epIsMatching()) { if (!$vars[$var]->epMatches($value, true)) { $this->epSetMatching(false); $o->epSetMatching(false); return false; } } else { $this->epSetMatching(false); $o->epSetMatching(false); return false; } } } } // reset matching flags $this->epSetMatching(false); $o->epSetMatching(false); return true; }
/** * Make where part of a SQL select to get children values * @param epDbObject $db the db connection * @param epObject $o the child object for query * @param int $depth how many children down we are * @param string $parent the parent of this child * @return array('from', 'where') * @author Oak Nauhygon <*****@*****.**> * @author Trevan Richins <*****@*****.**> */ public static function sqlSelectChildren($db, $o, $depth, $parent) { // array to keep new tables in 'from' $from = array(); // array to keep new expression in 'where' $where = array(); // get the class map for the child object $cm = $o->epGetClassMap(); // get all vars in the object $vars = $o->epGetVars(); // if object has oid, select use oid if ($oid = $o->epGetObjectId()) { $where[] = $db->quoteId($cm->getOidColumn()) . ' = ' . $oid; return array('from' => $from, 'where' => $where); } // mark child object under search (to avoid loops) $o->epSetSearching(true); // total number of vars (primitive or non-primitive) collected $n = 0; // new depth $depth++; // number of non-primitive (relationship) fields collected $nprim_id = 0; // loop through vars while (list($var, $val) = each($vars)) { // get field map if (!($fm =& $cm->getField($var))) { // should not happen continue; } // exclude null values (including empty strings) if (is_null($val) || !$val && $fm->getType() == epFieldMap::DT_CHAR) { continue; } // is it a primitive var? if ($fm->isPrimitive()) { $where[] = $db->quoteId($parent) . '.' . $db->quoteId($fm->getColumnName()) . ' = ' . $db->quote($val, $fm); // done for this var $n++; continue; } // okay we are dealing with a non-primitive (relationship) var if ($val instanceof epArray) { foreach ($val as $obj) { // skip object that is under searching if (!$obj || $obj->epIsSearching()) { continue; } // get 'where' and 'from' from relationship $from_where = epObj2Sql::sqlSelectRelations($db, $fm, $cm, $obj->epGetClassMap()->getTable(), $depth . $nprim_id, $parent); $where = array_merge($where, $from_where['where']); $from = array_merge($from, $from_where['from']); // get 'where' and 'from' from relationship $from_where = epObj2Sql::sqlSelectChildren($db, $obj, $depth, '_' . $depth . $nprim_id); $where = array_merge($where, $from_where['where']); $from = array_merge($from, $from_where['from']); $nprim_id++; } } else { if ($val instanceof epObject && !$val->epIsSearching()) { // get 'where' and 'from' from relationship $from_where = epObj2Sql::sqlSelectRelations($db, $fm, $cm, $val->epGetClassMap()->getTable(), $depth . $nprim_id, $parent); $where = array_merge($where, $from_where['where']); $from = array_merge($from, $from_where['from']); // get 'where' and 'from' from relationship $from_where = epObj2Sql::sqlSelectChildren($db, $val, $depth, '_' . $depth . $nprim_id); $where = array_merge($where, $from_where['where']); $from = array_merge($from, $from_where['from']); $nprim_id++; } } $n++; } // reset search flag on child object $o->epSetSearching(false); return array('from' => $from, 'where' => $where); }
/** * Update the value of the inverse var * @param epFieldMap * @param epObject $o the opposite object * @param string $actoin UPDATE_INVERSE_ADD or UPDATE_INVERSE_REMOVE * @return boolean */ protected function _updateInverse($fm, &$o, $action = self::UPDATE_INVERSE_ADD) { // check if object is epObject if (!$o || !$o instanceof epObject) { return false; } // no action if an object is updating (to prevent endless loop) if ($o->epIsUpdating()) { return true; } // get inverse var if (!($ivar = $fm->getInverse())) { return true; } // set inverse updating flag $o->epSetUpdating(true); // a single-valued field if (!$o->epIsMany($ivar)) { switch ($action) { case self::UPDATE_INVERSE_ADD: $o[$ivar] = $this; break; case self::UPDATE_INVERSE_REMOVE: $o[$ivar] = null; break; } } else { switch ($action) { case self::UPDATE_INVERSE_ADD: $o[$ivar][] = $this; break; case self::UPDATE_INVERSE_REMOVE: $o[$ivar]->remove($this); break; } } // reset inverse updating flag $o->epSetUpdating(false); return true; }
/** * Builds SQL statement from an object * @param epObject &$o the object * @param epFieldMap &$fm the field map of the var that the object is assigned to * @param string $alias * @return array */ protected function _buildSqlObject(epObject &$o, epFieldMap &$fm, $alias = self::DUMMY_ALIAS) { $o->epSetSearching(true); // get class map $cm = $this->em->getClassMap($fm->getClass()); // for a committed object, simply use OID if ($oid = $o->epGetObjectId()) { $sql = $this->qid($alias) . '.' . $this->qid($cm->getOidColumn()); $sql .= '=' . $this->q($oid); return array($sql); } // arrays to keep expressions for primitve and relationship vars $exprs_prm = array(''); $exprs_rel = array(''); // go through each var foreach ($o as $var => $val) { // get field map for var if (!($fm = $cm->getField($var))) { continue; } // primitive var if ($fm->isPrimitive() && !is_null($val)) { $expr = $this->qid($alias) . '.' . $this->qid($fm->getColumnName()); $expr .= '=' . $this->q($val, $fm); $exprs_prm[] = $expr; continue; } // relationship var if (!$val) { // done if empty value or has been visited continue; } // is it a many-valued var? $vals = $val; if (!$fm->isMany()) { // arrayize it if not an array $vals = array($val); } // go through each value in array foreach ($vals as $val) { // build sql for the relationship field map $exprs_rf = $this->_buildSqlFieldMapRelationship($fm, $alias); // is it an error message? if (is_string($exprs_rf)) { throw new epExceptionQueryBuilder($this->_e($exprs)); continue; } // concat sql for field map ($exprs_rf) and sql for var value ($exprs_rv) foreach ($exprs_rf as $alias_ => $expr_rf) { // recursion: build sql for the relationship var $exprs_rv = $this->_buildSqlObject($val, $fm, $alias_); // collect expression for relationship var foreach ($exprs_rv as $expr_rv) { $exprs_rel[] = $expr_rf . ' AND ' . $expr_rv; } } } } // array to hold final results $exprs_obj = array(); // concat sql for primitive and relationship fields foreach ($exprs_rel as $expr_rel) { foreach ($exprs_prm as $expr_prm) { if (!$expr_rel && $expr_prm) { $exprs_obj[] = $expr_prm; } else { if ($expr_rel && !$expr_prm) { $exprs_obj[] = $expr_rel; } else { if ($expr_rel && $expr_prm) { $exprs_obj[] = $expr_rel . ' AND ' . $expr_prm; } } } } } $o->epSetSearching(false); return $exprs_obj; }